diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 75ca2dfb9..63f79fa10 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,12 +1,48 @@ # Contributing +## Requirements + +Before getting started, ensure your system has access to the following tools: + +- [Node.js](https://nodejs.org/) +- [Rustup](https://rustup.rs/) +- [pnpm](https://pnpm.io/) + +## Getting started + +```sh +# Install dependencies +pnpm install + +# Install Rust toolchain and WASM targets +rustup default stable +rustup target add wasm32-wasip1-threads + +# Build the project +pnpm build +``` + +## Development workflow + +During development, you can run tests in watch mode: + +```sh +pnpm tdd +``` + +The `playgrounds` directory contains example projects you can use to test your changes. To start the Vite playground, use: + +```sh +pnpm build && pnpm vite +``` + ## Bug fixes If you've found a bug in Tailwind that you'd like to fix, [submit a pull request](https://github.com/tailwindlabs/tailwindcss/pulls) with your changes. Include a helpful description of the problem and how your changes address it, and provide tests so we can verify the fix works as expected. ## New features -If there's a new feature you'd like to see added to Tailwind, [share your idea with us](https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas) in our discussion forum to get it on our radar as something to consider for a future release. +If there's a new feature you'd like to see added to Tailwind, [share your idea with us](https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas) in our discussion forum to get it on our radar as something to consider for a future release before starting work on it. **Please note that we don't often accept pull requests for new features.** Adding a new feature to Tailwind requires us to think through the entire problem ourselves to make sure we agree with the proposed API, which means the feature needs to be high on our own priority list for us to be able to give it the attention it needs. @@ -14,7 +50,7 @@ If you open a pull request for a new feature, we're likely to close it not becau ## Coding standards -Our code formatting rules are defined in the `"prettier"` section of [package.json](https://github.com/tailwindcss/tailwindcss/blob/next/package.json). You can check your code against these standards by running: +Our code formatting rules are defined in the `"prettier"` section of [package.json](https://github.com/tailwindlabs/tailwindcss/blob/main/package.json). You can check your code against these standards by running: ```sh pnpm run lint @@ -28,10 +64,40 @@ pnpm run format ## Running tests -You can run the test suite using the following commands: +You can run the TypeScript and Rust test suites using the following command: ```sh -pnpm build && pnpm test +pnpm test ``` -Please ensure that the tests are passing when submitting a pull request. If you're adding new features to Tailwind, please include tests. +To run the integration tests, use: + +```sh +pnpm build && pnpm test:integrations +``` + +Additionally, some features require testing in browsers (i.e. to ensure CSS variable resolution works as expected). These can be run via: + +```sh +pnpm build && pnpm test:ui +``` + +Please ensure that all tests are passing when submitting a pull request. If you're adding new features to Tailwind CSS, always include tests. + +After a successful build, you can also use the npm package tarballs created inside the `dist/` folder to install your build in other local projects. + +## Pull request process + +When submitting a pull request: + +- Ensure the pull request title and description explain the changes you made and why you made them. +- Include a test plan section that outlines how you tested your contributions. We do not accept contributions without tests. +- Ensure all tests pass. You can add the tag `[ci-all]` in your pull request description to run the test suites across all platforms. + +When a pull request is created, Tailwind CSS maintainers will be notified automatically. + +## Communication + +- **GitHub discussions**: For feature ideas and general questions +- **GitHub issues**: For bug reports +- **GitHub pull requests**: For code contributions diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..848c851b1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://tailwindcss.com/sponsor'] diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000..1b8a310c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: If you've already asked for help with a problem and confirmed something is broken with Tailwind CSS itself, create a bug report. +title: '' +labels: '' +assignees: '' +--- + + + +**What version of Tailwind CSS are you using?** + +For example: v4.0.6 + +**What build tool (or framework if it abstracts the build tool) are you using?** + +For example: postcss-cli 11.0.0, Next.js 15.1.7, Vite 6.1.0 + +**What version of Node.js are you using?** + +For example: v20.0.0 + +**What browser are you using?** + +For example: Chrome, Safari, or N/A + +**What operating system are you using?** + +For example: macOS, Windows + +**Reproduction URL** + +A Tailwind Play link or public GitHub repo that includes a minimal reproduction of the bug. **Please do not link to your actual project**, what we need instead is a _minimal_ reproduction in a fresh project without any unnecessary code. This means it doesn't matter if your real project is private/confidential, since we want a link to a separate, isolated reproduction anyways. + +A reproduction is **required** when filing an issue — any issue opened without a reproduction will be closed and you'll be asked to create a new issue that includes a reproduction. We're a small team and we can't keep up with the volume of issues we receive if we need to reproduce each issue from scratch ourselves. + +**Describe your issue** + +Describe the problem you're seeing, any important steps to reproduce and what behavior you expect instead. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 58166a010..10ee9a6b8 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -6,9 +6,6 @@ contact_links: - name: Feature Request url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas about: 'Suggest any ideas you have using our discussion forums.' - - name: Bug Report - url: https://github.com/tailwindlabs/tailwindcss/issues/new?body=%3C%21--%20Please%20provide%20all%20of%20the%20information%20requested%20below.%20We%27re%20a%20small%20team%20and%20without%20all%20of%20this%20information%20it%27s%20not%20possible%20for%20us%20to%20help%20and%20your%20bug%20report%20will%20be%20closed.%20--%3E%0A%0A%2A%2AWhat%20version%20of%20Tailwind%20CSS%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20v2.0.4%0A%0A%2A%2AWhat%20build%20tool%20%28or%20framework%20if%20it%20abstracts%20the%20build%20tool%29%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20postcss-cli%208.3.1%2C%20Next.js%2010.0.9%2C%20webpack%205.28.0%0A%0A%2A%2AWhat%20version%20of%20Node.js%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20v12.0.0%0A%0A%2A%2AWhat%20browser%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20Chrome%2C%20Safari%2C%20or%20N%2FA%0A%0A%2A%2AWhat%20operating%20system%20are%20you%20using%3F%2A%2A%0A%0AFor%20example%3A%20macOS%2C%20Windows%0A%0A%2A%2AReproduction%20URL%2A%2A%0A%0AA%20Tailwind%20Play%20link%20or%20public%20GitHub%20repo%20that%20includes%20a%20minimal%20reproduction%20of%20the%20bug.%20%2A%2APlease%20do%20not%20link%20to%20your%20actual%20project%2A%2A%2C%20what%20we%20need%20instead%20is%20a%20_minimal_%20reproduction%20in%20a%20fresh%20project%20without%20any%20unnecessary%20code.%20This%20means%20it%20doesn%27t%20matter%20if%20your%20real%20project%20is%20private%2Fconfidential%2C%20since%20we%20want%20a%20link%20to%20a%20separate%2C%20isolated%20reproduction%20anyways.%0A%0AA%20reproduction%20is%20%2A%2Arequired%2A%2A%20when%20filing%20an%20issue%20%E2%80%94%20any%20issue%20opened%20without%20a%20reproduction%20will%20be%20closed%20and%20you%27ll%20be%20asked%20to%20create%20a%20new%20issue%20that%20includes%20a%20reproduction.%20We%27re%20a%20small%20team%20and%20we%20can%27t%20keep%20up%20with%20the%20volume%20of%20issues%20we%20receive%20if%20we%20need%20to%20reproduce%20each%20issue%20from%20scratch%20ourselves.%0A%0A%2A%2ADescribe%20your%20issue%2A%2A%0A%0ADescribe%20the%20problem%20you%27re%20seeing%2C%20any%20important%20steps%20to%20reproduce%20and%20what%20behavior%20you%20expect%20instead. - about: If you've already asked for help with a problem and confirmed something is broken with Tailwind CSS itself, create a bug report. - name: Documentation Issue url: https://github.com/tailwindlabs/tailwindcss.com about: 'For documentation issues, suggest changes on our documentation repository.' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ce7fafb97..aa0793aa3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,8 +4,26 @@ **Please ask first before starting work on any significant new features.** -It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create an issue to first discuss any significant new features. This includes things like adding new utilities, creating new at-rules, or adding new component examples to the documentation. +It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features. -https://github.com/tailwindcss/tailwindcss/blob/master/.github/CONTRIBUTING.md +For more info, check out the contributing guide: + +https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md + +--> + +## Summary + + + +## Test plan + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54512c071..5ae59e80f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,18 +2,24 @@ name: CI on: push: - branches: [next] + branches: [main] pull_request: permissions: contents: read +env: + NODE_VERSION: 24 + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: strategy: fail-fast: false matrix: - node-version: [20] runner: - name: Windows os: windows-latest @@ -21,17 +27,19 @@ jobs: - name: Linux os: namespace-profile-default + # Playwright 1.62+ dropped WebKit support for macOS 14, and hangs + # instead of failing when launching WebKit on a macos-14 runner. - name: macOS - os: macos-14 + os: macos-15 # Exclude windows and macos from being built on feature branches - on-next-branch: - - ${{ github.ref == 'refs/heads/next' }} + run-all: + - ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.body, '[ci-all]') || github.event.pull_request.user.login == 'depfu[bot]' }} exclude: - - on-next-branch: false + - run-all: false runner: name: Windows - - on-next-branch: false + - run-all: false runner: name: macOS @@ -41,21 +49,21 @@ jobs: name: ${{ matrix.runner.name }} steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' + persist-credentials: false + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ env.NODE_VERSION }} # Cargo already skips downloading dependencies if they already exist - name: Cache cargo - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | - ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ @@ -64,17 +72,25 @@ jobs: # Cache the `oxide` Rust build - name: Cache oxide build - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | - ./target/ ./crates/node/*.node - ./crates/node/index.js + ./crates/node/*.wasm ./crates/node/index.d.ts + ./crates/node/index.js + ./crates/node/browser.js + ./crates/node/tailwindcss-oxide.wasi-browser.js + ./crates/node/tailwindcss-oxide.wasi.cjs + ./crates/node/wasi-worker-browser.mjs + ./crates/node/wasi-worker.mjs key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }} + - name: Setup WASM target + run: rustup target add wasm32-wasip1-threads + - name: Install dependencies - run: pnpm install + run: pnpm install --frozen-lockfile - name: Build run: pnpm run build @@ -96,9 +112,13 @@ jobs: - name: Run Playwright tests run: npm run test:ui + notify: + if: ${{ always() && github.ref == 'refs/heads/main' && needs.tests.result == 'failure' }} + needs: tests + runs-on: ubuntu-latest + steps: - name: Notify Discord - if: failure() && github.ref == 'refs/heads/next' - uses: discord-actions/message@v2 + uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2 with: webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }} - message: 'The [most recent build](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `next` branch has failed.' + message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.' diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index d61223ae7..457c38424 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -2,19 +2,24 @@ name: Integration Tests on: push: - branches: [next] + branches: [main] pull_request: permissions: contents: read +env: + NODE_VERSION: 24 + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests: strategy: fail-fast: false matrix: - node-version: [20] - runner: - name: Windows os: windows-latest @@ -30,15 +35,17 @@ jobs: - vite - cli - postcss + - oxide + - webpack # Exclude windows and macos from being built on feature branches - on-next-branch: - - ${{ github.ref == 'refs/heads/next' }} + run-all: + - ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.body, '[ci-all]') }} exclude: - - on-next-branch: false + - run-all: false runner: name: Windows - - on-next-branch: false + - run-all: false runner: name: macOS @@ -48,21 +55,25 @@ jobs: name: ${{ matrix.runner.name }} / ${{ matrix.integration }} steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' + persist-credentials: false + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + + - run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ env.NODE_VERSION }} # Cargo already skips downloading dependencies if they already exist - name: Cache cargo - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | - ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ @@ -71,15 +82,23 @@ jobs: # Cache the `oxide` Rust build - name: Cache oxide build - uses: actions/cache@v4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | - ./target/ ./crates/node/*.node - ./crates/node/index.js + ./crates/node/*.wasm ./crates/node/index.d.ts + ./crates/node/index.js + ./crates/node/browser.js + ./crates/node/tailwindcss-oxide.wasi-browser.js + ./crates/node/tailwindcss-oxide.wasi.cjs + ./crates/node/wasi-worker-browser.mjs + ./crates/node/wasi-worker.mjs key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }} + - name: Setup WASM target + run: rustup target add wasm32-wasip1-threads + - name: Install dependencies run: pnpm install @@ -94,9 +113,13 @@ jobs: env: GITHUB_WORKSPACE: ${{ github.workspace }} + notify: + if: ${{ always() && github.ref == 'refs/heads/main' && needs.tests.result == 'failure' }} + needs: tests + runs-on: ubuntu-latest + steps: - name: Notify Discord - if: failure() && github.ref == 'refs/heads/next' - uses: discord-actions/message@v2 + uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2 with: webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }} - message: 'The [most recent build](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `next` branch has failed.' + message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.' diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 0c4979b2b..24e829afd 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -2,21 +2,306 @@ name: Prepare Release on: workflow_dispatch: + inputs: + dry_run: + description: Skip creating the draft GitHub release + required: false + default: true + type: boolean + push: + tags: + - 'v*' env: - CI: true + APP_NAME: tailwindcss-oxide + NODE_VERSION: 24 + PNPM_VERSION: '11.9.0' + OXIDE_LOCATION: ./crates/node permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: - runs-on: macos-12 - timeout-minutes: 15 - strategy: matrix: - node-version: [16] + include: + # Windows + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: windows-latest + target: aarch64-pc-windows-msvc + # macOS + - os: macos-latest + target: x86_64-apple-darwin + strip: strip -x # Must use -x on macOS. This produces larger results on linux. + - os: macos-latest + target: aarch64-apple-darwin + page-size: 14 + strip: strip -x # Must use -x on macOS. This produces larger results on linux. + # Android + - os: ubuntu-latest + target: aarch64-linux-android + strip: ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip + - os: ubuntu-latest + target: armv7-linux-androideabi + strip: ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip + # Linux + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + strip: strip + build-flags: --use-napi-cross + - os: ubuntu-latest + target: aarch64-unknown-linux-gnu + strip: aarch64-linux-gnu-strip + build-flags: --use-napi-cross + - os: ubuntu-latest + target: armv7-unknown-linux-gnueabihf + strip: arm-linux-gnueabihf-strip + build-flags: --use-napi-cross + - os: ubuntu-latest + target: aarch64-unknown-linux-musl + strip-zig: true + build-flags: -x + - os: ubuntu-latest + target: x86_64-unknown-linux-musl + strip: strip + build-flags: -x + + name: Build ${{ matrix.target }} (oxide) + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ env.NODE_VERSION }} + package-manager-cache: false + + - name: Install gcc-arm-linux-gnueabihf + if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }} + run: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y + + - name: Install binutils-aarch64-linux-gnu + if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }} + run: | + sudo apt-get update + sudo apt-get install binutils-aarch64-linux-gnu -y + + - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 + if: ${{ contains(matrix.target, 'musl') }} + with: + version: 0.14.1 + use-cache: false + + - name: Install cargo-zigbuild + uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2 + if: ${{ contains(matrix.target, 'musl') }} + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tool: cargo-zigbuild + + - name: Setup rust target + run: rustup target add ${{ matrix.target }} + + - name: Install dependencies + run: pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* + + - name: Build release + run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }} ${{ matrix.build-flags }} + env: + RUST_TARGET: ${{ matrix.target }} + JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }} + + - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034 + if: ${{ matrix.strip || matrix.strip-zig }} + env: + STRIP_COMMAND: ${{ matrix.strip }} + STRIP_ZIG: ${{ matrix.strip-zig }} + run: | + if [ "$STRIP_ZIG" = "true" ]; then + for file in ${{ env.OXIDE_LOCATION }}/*.node; do + zig objcopy --strip-all "$file" "$file.stripped" + mv "$file.stripped" "$file" + done + exit 0 + fi + + eval "$STRIP_COMMAND ${{ env.OXIDE_LOCATION }}/*.node" + + - name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: bindings-${{ matrix.target }} + path: ${{ env.OXIDE_LOCATION }}/*.node + + build-freebsd: + name: Build x86_64-unknown-freebsd (OXIDE) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - name: Build FreeBSD + uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0 + env: + DEBUG: napi:* + RUSTUP_HOME: /usr/local/rustup + CARGO_HOME: /usr/local/cargo + RUSTUP_IO_THREADS: 1 + RUST_TARGET: x86_64-unknown-freebsd + with: + operating_system: freebsd + version: '14.0' + memory: 13G + cpu_count: 3 + environment_variables: 'DEBUG RUSTUP_IO_THREADS' + shell: bash + run: | + sudo pkg install -y -f curl node libnghttp2 npm + sudo npm install -g pnpm@${{ env.PNPM_VERSION }} --unsafe-perm=true + curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init + chmod +x rustup-init + ./rustup-init -y --profile minimal + source "$HOME/.cargo/env" + pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* || true + echo "~~~~ rustc --version ~~~~" + rustc --version + echo "~~~~ node -v ~~~~" + node -v + echo "~~~~ pnpm --version ~~~~" + pnpm --version + pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform + strip -x ${{ env.OXIDE_LOCATION }}/*.node + ls -la ${{ env.OXIDE_LOCATION }} + - name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: bindings-x86_64-unknown-freebsd + path: ${{ env.OXIDE_LOCATION }}/*.node + + prepare: + runs-on: macos-14 + timeout-minutes: 15 + name: Build and release Tailwind CSS + + permissions: + contents: write # Required for creating releases + + needs: + - build + - build-freebsd steps: - - run: echo "stub" + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 20 + persist-credentials: false + + - run: git fetch --tags -f + + - name: Resolve version + id: vars + run: | + echo "TAG_NAME=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV + + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Use Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: 'https://registry.npmjs.org' + package-manager-cache: false + + - name: Setup WASM target + run: rustup target add wasm32-wasip1-threads + + - name: Install dependencies + run: pnpm --filter=!./playgrounds/* install --frozen-lockfile + + - name: Download artifacts + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 + with: + path: ${{ env.OXIDE_LOCATION }} + + - name: Move artifacts + run: | + cd ${{ env.OXIDE_LOCATION }} + cp bindings-x86_64-pc-windows-msvc/* ./npm/win32-x64-msvc/ + cp bindings-aarch64-pc-windows-msvc/* ./npm/win32-arm64-msvc/ + cp bindings-x86_64-apple-darwin/* ./npm/darwin-x64/ + cp bindings-aarch64-apple-darwin/* ./npm/darwin-arm64/ + cp bindings-aarch64-linux-android/* ./npm/android-arm64/ + cp bindings-armv7-linux-androideabi/* ./npm/android-arm-eabi/ + cp bindings-aarch64-unknown-linux-gnu/* ./npm/linux-arm64-gnu/ + cp bindings-aarch64-unknown-linux-musl/* ./npm/linux-arm64-musl/ + cp bindings-armv7-unknown-linux-gnueabihf/* ./npm/linux-arm-gnueabihf/ + cp bindings-x86_64-unknown-linux-gnu/* ./npm/linux-x64-gnu/ + cp bindings-x86_64-unknown-linux-musl/* ./npm/linux-x64-musl/ + cp bindings-x86_64-unknown-freebsd/* ./npm/freebsd-x64/ + + - name: Build Tailwind CSS + run: pnpm run build + env: + FEATURES_ENV: stable + + - name: Run pre-publish optimizations scripts + run: node ./scripts/pre-publish-optimizations.mjs + + - name: Lock pre-release versions + run: node ./scripts/lock-pre-release-versions.mjs + + - name: Get release notes + run: | + RELEASE_NOTES=$(node ./scripts/release-notes.mjs) + echo "RELEASE_NOTES<> $GITHUB_ENV + echo "$RELEASE_NOTES" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Upload standalone artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: tailwindcss-standalone + path: packages/@tailwindcss-standalone/dist/ + + - name: Upload npm package tarballs + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: npm-package-tarballs + path: dist/*.tgz + + - name: Prepare GitHub Release + if: ${{ !inputs.dry_run }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "${TAG_NAME}" \ + --draft \ + --title "${TAG_NAME}" \ + --notes "${RELEASE_NOTES}" \ + packages/@tailwindcss-standalone/dist/sha256sums.txt \ + packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64 \ + packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64-musl \ + packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64 \ + packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64-musl \ + packages/@tailwindcss-standalone/dist/tailwindcss-macos-arm64 \ + packages/@tailwindcss-standalone/dist/tailwindcss-macos-x64 \ + packages/@tailwindcss-standalone/dist/tailwindcss-windows-x64.exe diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64abf817a..2c957d3af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,22 +1,34 @@ name: Release on: + push: + branches: [main] + release: + types: [published] workflow_dispatch: inputs: - release_channel: - description: 'Release channel' - required: false - default: 'next' - type: string + channel: + description: Release channel to publish + required: true + default: insiders + type: choice + options: + - insiders + - release permissions: contents: read env: APP_NAME: tailwindcss-oxide - NODE_VERSION: 20 + NODE_VERSION: 24 + PNPM_VERSION: '11.9.0' OXIDE_LOCATION: ./crates/node +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: strategy: @@ -46,163 +58,207 @@ jobs: - os: ubuntu-latest target: x86_64-unknown-linux-gnu strip: strip - container: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + build-flags: --use-napi-cross - os: ubuntu-latest target: aarch64-unknown-linux-gnu - strip: llvm-strip - container: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 + strip: aarch64-linux-gnu-strip + build-flags: --use-napi-cross - os: ubuntu-latest target: armv7-unknown-linux-gnueabihf - strip: llvm-strip - container: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig + strip: arm-linux-gnueabihf-strip + build-flags: --use-napi-cross - os: ubuntu-latest target: aarch64-unknown-linux-musl - strip: aarch64-linux-musl-strip - download: true - container: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + strip-zig: true + build-flags: -x - os: ubuntu-latest target: x86_64-unknown-linux-musl strip: strip - download: true - container: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build-flags: -x - name: Build ${{ matrix.target }} (OXIDE) + name: Build ${{ matrix.target }} (oxide) runs-on: ${{ matrix.os }} - container: ${{ matrix.container }} timeout-minutes: 15 steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + with: + version: ${{ env.PNPM_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' + package-manager-cache: false - # Cargo already skips downloading dependencies if they already exist - - name: Cache cargo - uses: actions/cache@v4 - 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@v4 - with: - path: | - ./oxide/target/ - ./crates/node/*.node - ./crates/node/index.js - ./crates/node/index.d.ts - key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }} - - - name: Install Node.JS - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - - - name: Install Rust (Stable) - if: ${{ matrix.download }} + - name: Install gcc-arm-linux-gnueabihf + if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }} run: | - rustup default stable + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y + + - name: Install binutils-aarch64-linux-gnu + if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }} + run: | + sudo apt-get update + sudo apt-get install binutils-aarch64-linux-gnu -y + + - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 + if: ${{ contains(matrix.target, 'musl') }} + with: + version: 0.14.1 + use-cache: false + + - name: Install cargo-zigbuild + uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2 + if: ${{ contains(matrix.target, 'musl') }} + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tool: cargo-zigbuild - name: Setup rust target run: rustup target add ${{ matrix.target }} - name: Install dependencies - run: pnpm install --ignore-scripts --filter=!./playgrounds/* + run: pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* - name: Build release - run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build + run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }} ${{ matrix.build-flags }} env: RUST_TARGET: ${{ matrix.target }} JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }} - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034 - if: ${{ matrix.strip }} - run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node + if: ${{ matrix.strip || matrix.strip-zig }} + env: + STRIP_COMMAND: ${{ matrix.strip }} + STRIP_ZIG: ${{ matrix.strip-zig }} + run: | + if [ "$STRIP_ZIG" = "true" ]; then + for file in ${{ env.OXIDE_LOCATION }}/*.node; do + zig objcopy --strip-all "$file" "$file.stripped" + mv "$file.stripped" "$file" + done + exit 0 + fi + + eval "$STRIP_COMMAND ${{ env.OXIDE_LOCATION }}/*.node" - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: bindings-${{ matrix.target }} path: ${{ env.OXIDE_LOCATION }}/*.node + build-freebsd: + name: Build x86_64-unknown-freebsd (OXIDE) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - name: Build FreeBSD + uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0 + env: + DEBUG: napi:* + RUSTUP_HOME: /usr/local/rustup + CARGO_HOME: /usr/local/cargo + RUSTUP_IO_THREADS: 1 + RUST_TARGET: x86_64-unknown-freebsd + with: + operating_system: freebsd + version: '14.0' + memory: 13G + cpu_count: 3 + environment_variables: 'DEBUG RUSTUP_IO_THREADS' + shell: bash + run: | + sudo pkg install -y -f curl node libnghttp2 npm + sudo npm install -g pnpm@${{ env.PNPM_VERSION }} --unsafe-perm=true + curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init + chmod +x rustup-init + ./rustup-init -y --profile minimal + source "$HOME/.cargo/env" + echo "~~~~ rustc --version ~~~~" + rustc --version + echo "~~~~ node -v ~~~~" + node -v + echo "~~~~ pnpm --version ~~~~" + pnpm --version + pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* || true + pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform + strip -x ${{ env.OXIDE_LOCATION }}/*.node + ls -la ${{ env.OXIDE_LOCATION }} + - name: Upload artifacts + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + with: + name: bindings-x86_64-unknown-freebsd + path: ${{ env.OXIDE_LOCATION }}/*.node + release: runs-on: macos-14 timeout-minutes: 15 - name: Build and release Tailwind CSS + name: Build and publish Tailwind CSS permissions: - contents: write # for softprops/action-gh-release to create GitHub release + contents: read # https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions id-token: write needs: - build + - build-freebsd steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: - fetch-tags: true fetch-depth: 20 + persist-credentials: false - - run: git fetch --tags -f - - - name: Resolve version - id: vars - run: | - echo "TAG_NAME=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV - - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 + with: + version: ${{ env.PNPM_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' registry-url: 'https://registry.npmjs.org' + package-manager-cache: false - # Cargo already skips downloading dependencies if they already exist - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }} + # npm trusted publishing validates the caller workflow filename, so all npm publishes live here. + # This workflow rebuilds the publish artifacts instead of depending on prepare-release.yml. + - name: Resolve release metadata + env: + INPUT_CHANNEL: ${{ github.event.inputs.channel || '' }} + run: | + if [[ "${{ github.event_name }}" == "release" || "$INPUT_CHANNEL" == "release" ]]; then + release_channel=$(node ./scripts/release-channel.js) - # Cache the `oxide` Rust build - - name: Cache oxide build - uses: actions/cache@v4 - with: - path: | - ./oxide/target/ - ./crates/node/*.node - ./crates/node/index.js - ./crates/node/index.d.ts - key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }} + echo "RELEASE_KIND=release" >> $GITHUB_ENV + echo "RELEASE_CHANNEL=$release_channel" >> $GITHUB_ENV + echo "FEATURES_ENV=stable" >> $GITHUB_ENV + else + sha_short=$(git rev-parse --short HEAD) + + echo "RELEASE_KIND=insiders" >> $GITHUB_ENV + echo "RELEASE_CHANNEL=insiders" >> $GITHUB_ENV + echo "SHA_SHORT=$sha_short" >> $GITHUB_ENV + echo "INSIDERS_VERSION=0.0.0-insiders.$sha_short" >> $GITHUB_ENV + fi + + - name: Setup WASM target + run: rustup target add wasm32-wasip1-threads - name: Install dependencies - run: pnpm --filter=!./playgrounds/* install + run: pnpm --filter=!./playgrounds/* install --frozen-lockfile - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7 with: path: ${{ env.OXIDE_LOCATION }} @@ -220,52 +276,83 @@ jobs: cp bindings-armv7-unknown-linux-gnueabihf/* ./npm/linux-arm-gnueabihf/ cp bindings-x86_64-unknown-linux-gnu/* ./npm/linux-x64-gnu/ cp bindings-x86_64-unknown-linux-musl/* ./npm/linux-x64-musl/ + cp bindings-x86_64-unknown-freebsd/* ./npm/freebsd-x64/ + + - name: 'Version based on commit: ${{ env.INSIDERS_VERSION }}' + if: env.RELEASE_KIND == 'insiders' + run: pnpm run version-packages ${INSIDERS_VERSION} - name: Build Tailwind CSS + if: env.RELEASE_KIND == 'insiders' run: pnpm run build + - name: Build Tailwind CSS + if: env.RELEASE_KIND == 'release' + run: pnpm run build + env: + FEATURES_ENV: ${{ env.FEATURES_ENV }} + - name: Run pre-publish optimizations scripts run: node ./scripts/pre-publish-optimizations.mjs - name: Lock pre-release versions run: node ./scripts/lock-pre-release-versions.mjs - - name: Get release notes - run: | - RELEASE_NOTES=$(node ./scripts/release-notes.mjs) - echo "RELEASE_NOTES<> $GITHUB_ENV - echo "$RELEASE_NOTES" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - - name: Upload Standalone Artifacts - uses: actions/upload-artifact@v4 + - name: Upload npm package tarballs + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: - name: tailwindcss-standalone - path: packages/@tailwindcss-standalone/dist/ + name: npm-package-tarballs + path: dist/*.tgz - name: Publish - run: pnpm --recursive publish --tag ${{ inputs.release_channel }} --no-git-checks - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Alias packages to `latest` - if: ${{ inputs.release_channel == "next" }} run: | - npm dist-tag add @tailwindcss/upgrade@${{ env.TAG_NAME }} latest - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + pnpm --recursive --filter="!@tailwindcss/oxide-wasm32-wasi" publish --tag ${RELEASE_CHANNEL} --no-git-checks + # The wasm package needs a special npm config that isn't read when pnpm --recursive is used + pushd crates/node/npm/wasm32-wasi; pnpm publish --tag ${RELEASE_CHANNEL} --no-git-checks --config.node-linker=hoisted; popd; - - name: Release - uses: softprops/action-gh-release@v2 + - name: Trigger Tailwind Play update + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: - draft: true - tag_name: ${{ env.TAG_NAME }} - body: | - ${{ env.RELEASE_NOTES }} - files: | - packages/@tailwindcss-standalone/dist/sha256sums.txt - packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64 - packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64 - packages/@tailwindcss-standalone/dist/tailwindcss-macos-arm64 - packages/@tailwindcss-standalone/dist/tailwindcss-macos-x64 - packages/@tailwindcss-standalone/dist/tailwindcss-windows-x64.exe + github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: 'tailwindlabs', + repo: 'upgrades', + ref: 'main', + workflow_id: 'upgrade-tailwindcss.yml' + }) + + notify: + if: ${{ always() && (needs.build.result == 'failure' || needs.build-freebsd.result == 'failure' || needs.release.result == 'failure') }} + needs: + - build + - build-freebsd + - release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + + - name: Resolve release label + id: release + env: + INPUT_CHANNEL: ${{ github.event.inputs.channel || '' }} + RELEASE_TAG: ${{ github.event.release.tag_name || '' }} + run: | + if [[ "${{ github.event_name }}" == "release" ]]; then + tag_name="${RELEASE_TAG:-${GITHUB_REF_NAME}}" + echo "label=release ${tag_name}" >> $GITHUB_OUTPUT + elif [[ "$INPUT_CHANNEL" == "release" ]]; then + version=$(node -p "require('./packages/tailwindcss/package.json').version") + echo "label=release v${version}" >> $GITHUB_OUTPUT + else + sha_short=$(git rev-parse --short HEAD) + echo "label=insiders release 0.0.0-insiders.${sha_short}" >> $GITHUB_OUTPUT + fi + + - name: Notify Discord + uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2 + with: + webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }} + message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) for `${{ steps.release.outputs.label }}` has failed.' diff --git a/.gitignore b/.gitignore index 9d75bba27..bbac7d227 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ playwright-report/ blob-report/ playwright/.cache/ target/ -.debug +.debug/ diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ded82e2f6..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -auto-install-peers = true diff --git a/.prettierignore b/.prettierignore index 3de4530dd..4f50b932f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,5 +4,6 @@ pnpm-lock.yaml target/ crates/node/index.d.ts crates/node/index.js +crates/ignore/ .next .fingerprint diff --git a/CHANGELOG.md b/CHANGELOG.md index a6787ae2d..e5e6f6f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,896 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Added + +- Add `@tailwindcss/turbopack` package to run Tailwind CSS with Next.js ([20367](https://github.com/tailwindlabs/tailwindcss/pull/20367)) + +### Fixed + +- Ensure watch mode detects changes to symlinked `@source` files whose real paths aren't otherwise scanned ([#20356](https://github.com/tailwindlabs/tailwindcss/pull/20356)) +- Ensure custom variants using `@scope` wrap the generated utilities instead of nesting inside them ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369)) +- Fix flattening of `@scope` at-rules ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369)) +- Fix standalone declarations in `@scope`, wrap them in `:where(:scope)` ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369)) +- Always emit a space for empty fallback values in CSS variables (e.g. `var(--tw-blur,)` → `var(--tw-blur, )`) ([#20373](https://github.com/tailwindlabs/tailwindcss/pull/20373)) +- Canonicalization: convert arbitrary breakpoint and container query variants to named equivalents (e.g. `max-[64rem]` → `max-lg`) ([#20380](https://github.com/tailwindlabs/tailwindcss/pull/20380)) +- Prevent `@tailwindcss/vite` from crashing on every edit under Vite's experimental `bundledDev` mode ([#20379](https://github.com/tailwindlabs/tailwindcss/pull/20379)) +- Ensure `@tailwindcss/oxide` falls back to WASM on platforms without native bindings ([#20383](https://github.com/tailwindlabs/tailwindcss/pull/20383)) +- Detect classes in Ruby percent literals using angle brackets or custom delimiters (e.g. `%w`, `%w|flex|`), including in Slim and Haml templates ([#20387](https://github.com/tailwindlabs/tailwindcss/pull/20387)) +- Preserve whitespace in `--default(…)` values in custom functional utilities (e.g. `--default(box alphabetic)` no longer becomes `boxalphabetic`) ([#20392](https://github.com/tailwindlabs/tailwindcss/pull/20392)) +- Don't scan gitignored directories (e.g. `node_modules` and `.git`) when the project uses a safelist-style `.gitignore` (e.g. `/*` followed by `!/…` negations) ([#20397](https://github.com/tailwindlabs/tailwindcss/pull/20397)) +- Ensure root `theme('…')` namespace lookups in JavaScript plugins and config files return the full namespace object instead of the value of its `DEFAULT` key ([#20399](https://github.com/tailwindlabs/tailwindcss/pull/20399)) +- Skip ignored directories entirely when computing watch globs (`scanner.globs`), instead of walking their full contents on every rebuild ([#20408](https://github.com/tailwindlabs/tailwindcss/pull/20408)) +- Oxide: drop invalid UTF-8 candidates ([#20389](https://github.com/tailwindlabs/tailwindcss/pull/20389)) +- `@tailwindcss/vite` no longer forces a full page reload for external files (e.g.: `.php` files) ([#20414](https://github.com/tailwindlabs/tailwindcss/issues/20414)) +- Canonicalization: don't merge utilities that reference different theme variables set to CSS-wide keywords like `unset` ([#20417](https://github.com/tailwindlabs/tailwindcss/pull/20417)) +- Don't generate utilities when a modifier is used that would otherwise be silently ignored (e.g. `rounded-sm/[5]`, `shadow-sm/foo`, `stroke-2/50`) ([#20419](https://github.com/tailwindlabs/tailwindcss/pull/20419)) +- Only normalize top-level `and`, `or`, and `not` keywords in `supports-[…]` variants (e.g. `selector(a: not (.foo))` → `selector(a:not(.foo))`) ([#20420](https://github.com/tailwindlabs/tailwindcss/pull/20420)) +- Don't warn about Angular's `::ng-deep` and `:host-context()` when optimizing CSS ([#20434](https://github.com/tailwindlabs/tailwindcss/pull/20434)) +- Don't generate CSS for candidates containing an empty additional modifier (e.g. `bg-red-500/50/` and `group-hover/foo//bar:flex`) ([#20466](https://github.com/tailwindlabs/tailwindcss/pull/20466)) +- Sort `min-*`, `max-*`, and container query variants with decimal values numerically (e.g. `min-[40.25rem]` before `min-[40.5rem]`) ([#20512](https://github.com/tailwindlabs/tailwindcss/pull/20512)) +- Ensure CSS comments ending with `\*/` are closed correctly instead of swallowing the CSS that follows (e.g. `/* C:\temp\*/`) ([#20508](https://github.com/tailwindlabs/tailwindcss/pull/20508)) +- Improve style invalidation performance of `group-*` and `peer-*` variants ([#20513](https://github.com/tailwindlabs/tailwindcss/pull/20513)) + +## [4.3.3] - 2026-07-16 + +### Fixed + +- Support `--watch --poll[=ms]` in `@tailwindcss/cli` when filesystem events are unreliable or unavailable ([#20297](https://github.com/tailwindlabs/tailwindcss/pull/20297)) +- Canonicalization: match arbitrary hex colors against theme colors case-insensitively (e.g. `bg-[#fff]` and `bg-[#FFF]` → `bg-white`) ([#20298](https://github.com/tailwindlabs/tailwindcss/pull/20298)) +- Prevent Preflight from overriding Firefox's native `iframe:focus-visible` outline styles ([#20292](https://github.com/tailwindlabs/tailwindcss/pull/20292)) +- Ensure `theme('colors.foo')` in JS plugins resolves correctly when both `--color-foo` and `--color-foo-bar` exist ([#20299](https://github.com/tailwindlabs/tailwindcss/pull/20299)) +- Ensure fractional opacity modifiers work with named shadow sizes like `shadow-sm/12.5`, `text-shadow-sm/12.5`, `drop-shadow-sm/12.5`, and `inset-shadow-sm/12.5` ([#20302](https://github.com/tailwindlabs/tailwindcss/pull/20302)) +- Parse selectors like `[data-foo]div` as two selectors instead of one ([#20303](https://github.com/tailwindlabs/tailwindcss/pull/20303)) +- Ensure `@tailwindcss/postcss` rebuilds when a preprocessor like Sass changes the input CSS without changing the input file on disk ([#20310](https://github.com/tailwindlabs/tailwindcss/pull/20310)) +- Ensure CSS nesting is handled even when Lightning CSS isn't run, such as in `@tailwindcss/browser` and Tailwind Play ([#20124](https://github.com/tailwindlabs/tailwindcss/pull/20124)) +- Prevent achromatic theme colors from shifting hue when mixed in polar color spaces like `oklch` ([#20314](https://github.com/tailwindlabs/tailwindcss/pull/20314)) +- Ensure `--spacing(0)` is optimized to `0px` instead of `0` so it remains a `` when used in `calc(…)` ([#20319](https://github.com/tailwindlabs/tailwindcss/pull/20319)) +- Load `@parcel/watcher` only when needed in `@tailwindcss/cli --watch` mode, so one-off builds and `--watch --poll` work when `@parcel/watcher` can't be loaded ([#20325](https://github.com/tailwindlabs/tailwindcss/pull/20325)) +- Use explicit platform fonts instead of `system-ui` and `ui-sans-serif` so CJK text respects the page's `lang` attribute on Windows ([#20318](https://github.com/tailwindlabs/tailwindcss/pull/20318)) +- Prevent `@tailwindcss/upgrade` from rewriting ignored files when run from a subdirectory ([#20329](https://github.com/tailwindlabs/tailwindcss/pull/20329)) +- Ensure earlier `@source` rules pointing to nested files are scanned when later `@source` rules point to files in parent folders ([#20335](https://github.com/tailwindlabs/tailwindcss/pull/20335)) +- Prevent `@tailwindcss/vite` from triggering full page reloads when scanned files are processed by Vite but haven't been loaded as modules yet ([#20336](https://github.com/tailwindlabs/tailwindcss/pull/20336)) + +## [4.3.2] - 2026-06-26 + +### Fixed + +- Support bare spacing values for `auto-rows-*` and `auto-cols-*` utilities (e.g. `auto-rows-12` and `auto-cols-16`) ([#20229](https://github.com/tailwindlabs/tailwindcss/pull/20229)) +- Prevent `@tailwindcss/cli` in `--watch` mode from crashing on Windows when `@source` points to a directory that doesn't exist ([#20242](https://github.com/tailwindlabs/tailwindcss/pull/20242)) +- Prevent `@tailwindcss/vite` from crashing in Deno v2.8.x when `context.parentURL` is not a valid URL ([#20245](https://github.com/tailwindlabs/tailwindcss/pull/20245)) +- Ensure `@tailwindcss/cli` in `--watch` mode rebuilds when the input CSS file changes in an ignored directory ([#20246](https://github.com/tailwindlabs/tailwindcss/pull/20246)) +- Allow `@variant` rules used in `addBase(…)` to use custom variants defined later ([#20247](https://github.com/tailwindlabs/tailwindcss/pull/20247)) +- Prevent `@tailwindcss/vite` from crashing during HMR when scanned files or directories are deleted ([#20259](https://github.com/tailwindlabs/tailwindcss/pull/20259)) +- Generate `font-size` instead of `color` declarations for `text-[--spacing(…)]` ([#20260](https://github.com/tailwindlabs/tailwindcss/pull/20260)) +- Prevent `@source` patterns from scanning unrelated sibling files and folders ([#20263](https://github.com/tailwindlabs/tailwindcss/pull/20263)) +- Extract class candidates adjacent to Template Toolkit delimiters like `%]…[%` in `.tt`, `.tt2`, and `.tx` files ([#20269](https://github.com/tailwindlabs/tailwindcss/pull/20269)) +- Extract class candidates from conditional Maud syntax like `p.text-black[condition]` ([#20269](https://github.com/tailwindlabs/tailwindcss/pull/20269)) +- Prevent `@position-try` rules from triggering unknown at-rule warnings when optimizing CSS ([#20277](https://github.com/tailwindlabs/tailwindcss/pull/20277)) +- Support class suggestions for named opacity modifiers from `--opacity` theme values ([#20287](https://github.com/tailwindlabs/tailwindcss/pull/20287)) +- Prevent type errors in `@tailwindcss/postcss` when used with newer PostCSS patch releases ([#20289](https://github.com/tailwindlabs/tailwindcss/pull/20289)) + +## [4.3.1] - 2026-06-12 + +### Added + +- Add `--silent` option to suppress output in `@tailwindcss/cli` ([#20100](https://github.com/tailwindlabs/tailwindcss/pull/20100)) + +### Fixed + +- Remove deprecation warnings by using `Module#registerHooks` instead of `Module#register` on Node 26+ ([#20028](https://github.com/tailwindlabs/tailwindcss/pull/20028)) +- Canonicalization: don't crash when plugin utilities throw for unsupported values ([#20052](https://github.com/tailwindlabs/tailwindcss/pull/20052)) +- Allow `@apply` to be used with CSS mixins ([#19427](https://github.com/tailwindlabs/tailwindcss/pull/19427)) +- Ensure `not-*` correctly negates `@container` queries, including `style(…)` queries ([#20059](https://github.com/tailwindlabs/tailwindcss/pull/20059)) +- Ensure `drop-shadow-*` color utilities work with custom shadow values containing `calc(…)` ([#20080](https://github.com/tailwindlabs/tailwindcss/pull/20080)) +- Fix 'Sourcemap is likely to be incorrect' warnings when using `@tailwindcss/vite` ([#20103](https://github.com/tailwindlabs/tailwindcss/pull/20103)) +- Ensure `@tailwindcss/webpack` can be installed in Rspack projects without requiring `webpack` as a peer dependency ([#20027](https://github.com/tailwindlabs/tailwindcss/pull/20027)) +- Canonicalization: don't suggest invalid `calc(…)` expressions (e.g. `px-[calc(1rem+0px)]` → `px-[calc(1rem+0)]`) ([#20127](https://github.com/tailwindlabs/tailwindcss/pull/20127)) +- Canonicalization: avoid suggesting large spacing-scale values for arbitrary lengths (e.g. `left-[99999px]` → `left-[99999px]`, not `left-24999.75`) ([#20130](https://github.com/tailwindlabs/tailwindcss/pull/20130)) +- Ensure `@tailwindcss/cli` in `--watch` mode recovers when a tracked dependency is deleted and restored ([#20137](https://github.com/tailwindlabs/tailwindcss/pull/20137)) +- Ensure standalone `@tailwindcss/cli` binaries are ignored when scanning for class candidates ([#20139](https://github.com/tailwindlabs/tailwindcss/pull/20139)) +- Ensure class candidates are extracted from Twig `addClass(…)` and `removeClass(…)` calls ([#20198](https://github.com/tailwindlabs/tailwindcss/pull/20198)) +- Don't crash in the Ruby or Vue preprocessors when scanning files containing invalid UTF-8 bytes ([#19588](https://github.com/tailwindlabs/tailwindcss/pull/19588)) +- Allow `@variant` to be used inside `addBase` ([#19480](https://github.com/tailwindlabs/tailwindcss/pull/19480)) +- Ensure `@source` globs with symlinks are preserved ([#20203](https://github.com/tailwindlabs/tailwindcss/pull/20203)) +- Ensure later `@source` rules can re-include files excluded by earlier `@source not` rules ([#20203](https://github.com/tailwindlabs/tailwindcss/pull/20203)) +- Upgrade: don't migrate empty class rules to invalid `@utility` rules ([#20205](https://github.com/tailwindlabs/tailwindcss/pull/20205)) +- Ensure transitions between `inset-shadow-none` and other inset shadows work correctly ([#20208](https://github.com/tailwindlabs/tailwindcss/pull/20208)) +- Ensure explicitly referenced `@source` directories are scanned even when ignored by git ([#20214](https://github.com/tailwindlabs/tailwindcss/pull/20214)) +- Ensure `@source` globs ending in `**/*` preserve dynamic path segments to avoid scanning too many files ([#20217](https://github.com/tailwindlabs/tailwindcss/pull/20217)) +- Canonicalization: don't fold `calc(…)` divisions when the result would require high precision (e.g. `w-[calc(100%/3.5)]` → `w-[calc(100%/3.5)]`, not `w-[28.571428571428573%]`) ([#20221](https://github.com/tailwindlabs/tailwindcss/pull/20221)) +- Serve ESM type declarations to ESM importers of `@tailwindcss/postcss` ([#20228](https://github.com/tailwindlabs/tailwindcss/pull/20228)) + +### Changed + +- Generate `0` instead of `calc(var(--spacing) * 0)` for spacing utilities like `m-0` and `left-0` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196)) +- Generate `var(--spacing)` instead of `calc(var(--spacing) * 1)` for spacing utilities like `m-1` and `left-1` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196)) + +## [4.3.0] - 2026-05-08 + +### Added + +- Add `@container-size` utility ([#18901](https://github.com/tailwindlabs/tailwindcss/pull/18901)) +- Add `scrollbar-{auto,thin,none}` utilities for `scrollbar-width`, and `scrollbar-thumb-*` / `scrollbar-track-*` color utilities for `scrollbar-color` ([#19981](https://github.com/tailwindlabs/tailwindcss/pull/19981), [#20019](https://github.com/tailwindlabs/tailwindcss/pull/20019)) +- Add `scrollbar-gutter-*` utilities ([#20018](https://github.com/tailwindlabs/tailwindcss/pull/20018)) +- Add `zoom-*` utilities ([#20020](https://github.com/tailwindlabs/tailwindcss/pull/20020)) +- Add `tab-*` utilities ([#20022](https://github.com/tailwindlabs/tailwindcss/pull/20022)) +- Allow using `@variant` with stacked variants (e.g. `@variant hover:focus { … }`) ([#19996](https://github.com/tailwindlabs/tailwindcss/pull/19996)) +- Allow using `@variant` with compound variants (e.g. `@variant hover, focus { … }`) ([#19996](https://github.com/tailwindlabs/tailwindcss/pull/19996)) +- Support `--default(…)` in `--value(…)` and `--modifier(…)` for functional `@utility` definitions ([#19989](https://github.com/tailwindlabs/tailwindcss/pull/19989)) + +### Fixed + +- Ensure `@plugin` resolves package JavaScript entries instead of browser CSS entries when using `@tailwindcss/vite` ([#19949](https://github.com/tailwindlabs/tailwindcss/pull/19949)) +- Fix relative `@import` and `@plugin` paths resolving from the wrong directory when using `@tailwindcss/vite` ([#19965](https://github.com/tailwindlabs/tailwindcss/pull/19965)) +- Ensure CSS files containing `@variant` are processed by `@tailwindcss/vite` ([#19966](https://github.com/tailwindlabs/tailwindcss/pull/19966)) +- Resolve imports relative to `base` when `result.opts.from` is not provided when using `@tailwindcss/postcss` ([#19980](https://github.com/tailwindlabs/tailwindcss/pull/19980)) +- Canonicalization: preserve significant `_` whitespace in arbitrary values ([#19986](https://github.com/tailwindlabs/tailwindcss/pull/19986)) +- Canonicalization: add parentheses when removing whitespace from arbitrary values would hurt readability (e.g. `w-[calc(100%---spacing(60))]` → `w-[calc(100%-(--spacing(60)))]`) ([#19986](https://github.com/tailwindlabs/tailwindcss/pull/19986)) +- Canonicalization: preserve the original unit in arbitrary values instead of normalizing to base units (e.g. `-mt-[20in]` → `mt-[-20in]`, not `mt-[-1920px]`) ([#19988](https://github.com/tailwindlabs/tailwindcss/pull/19988)) +- Canonicalization: migrate arbitrary `:has()` variants from `[&:has(…)]` to `has-[…]` ([#19991](https://github.com/tailwindlabs/tailwindcss/pull/19991)) +- Upgrade: don’t migrate inline `style` attributes (e.g. `style="flex-grow: 1"` → `style="flex-grow: 1"`, not `style="grow: 1"`) ([#19918](https://github.com/tailwindlabs/tailwindcss/pull/19918)) +- Allow multiple `@utility` definitions with the same name but different value types ([#19777](https://github.com/tailwindlabs/tailwindcss/pull/19777)) +- Export missing `PluginWithConfig` type from `tailwindcss/plugin` to fix errors when inferring plugin config types ([#19707](https://github.com/tailwindlabs/tailwindcss/pull/19707)) +- Ensure `start` and `end` legacy utilities without values do not generate CSS ([#20003](https://github.com/tailwindlabs/tailwindcss/pull/20003)) +- Ensure `--value(…)` is required in functional `@utility` definitions ([#20005](https://github.com/tailwindlabs/tailwindcss/pull/20005)) +- Canonicalization: preserve required whitespace around operators in negated arbitrary values (e.g. `-left-[(var(--a)+var(--b))]`) ([#20011](https://github.com/tailwindlabs/tailwindcss/pull/20011)) + +## [4.2.4] - 2026-04-21 + +### Fixed + +- Ensure imports in `@import` and `@plugin` still resolve correctly when using Vite aliases in `@tailwindcss/vite` ([#19947](https://github.com/tailwindlabs/tailwindcss/pull/19947)) + +## [4.2.3] - 2026-04-20 + +### Fixed + +- Canonicalization: improve canonicalization for `tracking-*` utilities by preferring non-negative utilities (e.g. `-tracking-tighter` → `tracking-wider`) ([#19827](https://github.com/tailwindlabs/tailwindcss/pull/19827)) +- Fix crash due to invalid characters in candidate (exceeding valid unicode code point range) ([#19829](https://github.com/tailwindlabs/tailwindcss/pull/19829)) +- Ensure query params in imports are considered unique resources when using `@tailwindcss/webpack` ([#19723](https://github.com/tailwindlabs/tailwindcss/pull/19723)) +- Canonicalization: collapse arbitrary values into shorthand utilities (e.g. `px-[1.2rem] py-[1.2rem]` → `p-[1.2rem]`) ([#19837](https://github.com/tailwindlabs/tailwindcss/pull/19837)) +- Canonicalization: collapse `border-{t,b}-*` into `border-y-*`, `border-{l,r}-*` into `border-x-*`, and `border-{t,r,b,l}-*` into `border-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842)) +- Canonicalization: collapse `scroll-m{t,b}-*` into `scroll-my-*`, `scroll-m{l,r}-*` into `scroll-mx-*`, and `scroll-m{t,r,b,l}-*` into `scroll-m-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842)) +- Canonicalization: collapse `scroll-p{t,b}-*` into `scroll-py-*`, `scroll-p{l,r}-*` into `scroll-px-*`, and `scroll-p{t,r,b,l}-*` into `scroll-p-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842)) +- Canonicalization: collapse `overflow-{x,y}-*` into `overflow-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842)) +- Canonicalization: collapse `overscroll-{x,y}-*` into `overscroll-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842)) +- Read from `--placeholder-color` instead of `--background-color` for `placeholder-*` utilities ([#19843](https://github.com/tailwindlabs/tailwindcss/pull/19843)) +- Upgrade: ensure files are not emptied out when killing the upgrade process while it's running ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846)) +- Upgrade: use `config.content` when migrating from Tailwind CSS v3 to Tailwind CSS v4 ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846)) +- Upgrade: never migrate files that are ignored by git ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846)) +- Add `.env` and `.env.*` to default ignored content files ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846)) +- Canonicalization: migrate `overflow-ellipsis` into `text-ellipsis` ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849)) +- Canonicalization: migrate `start-full` → `inset-s-full`, `start-auto` → `inset-s-auto`, `start-px` → `inset-s-px`, and `start-` → `inset-s-` as well as negative versions ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849)) +- Canonicalization: migrate `end-full` → `inset-e-full`, `end-auto` → `inset-e-auto`, `end-px` → `inset-e-px`, and `end-` → `inset-e-` as well as negative versions ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849)) +- Canonicalization: move the `-` sign inside the arbitrary value `-left-[9rem]` → `left-[-9rem]` ([#19858](https://github.com/tailwindlabs/tailwindcss/pull/19858)) +- Canonicalization: move the `-` sign outside the arbitrary value `ml-[calc(-1*var(--width))]` → `-ml-(--width)` ([#19858](https://github.com/tailwindlabs/tailwindcss/pull/19858)) +- Improve performance when scanning JSONL / NDJSON files ([#19862](https://github.com/tailwindlabs/tailwindcss/pull/19862)) +- Support `NODE_PATH` environment variable in standalone CLI ([#19617](https://github.com/tailwindlabs/tailwindcss/pull/19617)) + +## [4.2.2] - 2026-03-18 + +### Fixed + +- Don't crash when candidates contain prototype properties like `row-constructor` ([#19725](https://github.com/tailwindlabs/tailwindcss/pull/19725)) +- Canonicalize `calc(var(--spacing)*…)` expressions into `--spacing(…)` ([#19769](https://github.com/tailwindlabs/tailwindcss/pull/19769)) +- Fix crash in canonicalization step when handling utilities containing `@property` at-rules (e.g. `shadow-sm border`) ([#19727](https://github.com/tailwindlabs/tailwindcss/pull/19727)) +- Skip full reload for server only modules scanned by client CSS when using `@tailwindcss/vite` ([#19745](https://github.com/tailwindlabs/tailwindcss/pull/19745)) +- Add support for Vite 8 in `@tailwindcss/vite` ([#19790](https://github.com/tailwindlabs/tailwindcss/pull/19790)) +- Improve canonicalization for bare values exceeding default spacing scale suggestions (e.g. `w-1234 h-1234` → `size-1234`) ([#19809](https://github.com/tailwindlabs/tailwindcss/pull/19809)) +- Fix canonicalization resulting in empty list (e.g. `w-5 h-5 size-5` → `''` instead of `size-5`) ([#19812](https://github.com/tailwindlabs/tailwindcss/pull/19812)) +- Resolve tsconfig paths to allow for `@import '@/path/to/file';` when using `@tailwindcss/vite` ([#19803](https://github.com/tailwindlabs/tailwindcss/pull/19803)) + +## [4.2.1] - 2026-02-23 + +### Fixed + +- Allow trailing dash in functional utility names for backwards compatibility ([#19696](https://github.com/tailwindlabs/tailwindcss/pull/19696)) +- Properly detect classes containing `.` characters within curly braces in MDX files ([#19711](https://github.com/tailwindlabs/tailwindcss/pull/19711)) + +## [4.2.0] - 2026-02-18 + +### Added + +- Add mauve, olive, mist, and taupe color palettes to the default theme ([#19627](https://github.com/tailwindlabs/tailwindcss/pull/19627)) +- Add `@tailwindcss/webpack` package to run Tailwind CSS as a webpack plugin ([#19610](https://github.com/tailwindlabs/tailwindcss/pull/19610)) +- Add `pbs-*` and `pbe-*` utilities for `padding-block-start` and `padding-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601)) +- Add `mbs-*` and `mbe-*` utilities for `margin-block-start` and `margin-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601)) +- Add `scroll-pbs-*` and `scroll-pbe-*` utilities for `scroll-padding-block-start` and `scroll-padding-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601)) +- Add `scroll-mbs-*` and `scroll-mbe-*` utilities for `scroll-margin-block-start` and `scroll-margin-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601)) +- Add `border-bs-*` and `border-be-*` utilities for `border-block-start` and `border-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601)) +- Add `inline-*`, `min-inline-*`, `max-inline-*` utilities for `inline-size`, `min-inline-size`, and `max-inline-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612)) +- Add `block-*`, `min-block-*`, `max-block-*` utilities for `block-size`, `min-block-size`, and `max-block-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612)) +- Add `inset-s-*`, `inset-e-*`, `inset-bs-*`, `inset-be-*` utilities for `inset-inline-start`, `inset-inline-end`, `inset-block-start`, and `inset-block-end` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) +- Add `font-features-*` utility for `font-feature-settings` ([#19623](https://github.com/tailwindlabs/tailwindcss/pull/19623)) + +### Fixed + +- Prevent double `@supports` wrapper for `color-mix` values ([#19450](https://github.com/tailwindlabs/tailwindcss/pull/19450)) +- Allow whitespace around `@source inline()` argument ([#19461](https://github.com/tailwindlabs/tailwindcss/pull/19461)) +- Emit comment when source maps are saved to files when using `@tailwindcss/cli` ([#19447](https://github.com/tailwindlabs/tailwindcss/pull/19447)) +- Detect utilities containing capital letters followed by numbers ([#19465](https://github.com/tailwindlabs/tailwindcss/pull/19465)) +- Fix class extraction for Rails' strict locals ([#19525](https://github.com/tailwindlabs/tailwindcss/pull/19525)) +- Align `@utility` name validation with Oxide scanner rules ([#19524](https://github.com/tailwindlabs/tailwindcss/pull/19524)) +- Fix infinite loop when using `@variant` inside `@custom-variant` ([#19633](https://github.com/tailwindlabs/tailwindcss/pull/19633)) +- Allow multiples of `.25` in `aspect-*` fractions (e.g. `aspect-8.5/11`) ([#19688](https://github.com/tailwindlabs/tailwindcss/pull/19688)) +- Ensure changes to external files listed via `@source` trigger a full page reload when using `@tailwindcss/vite` ([#19670](https://github.com/tailwindlabs/tailwindcss/pull/19670)) +- Improve performance of Oxide scanner in bigger projects by reducing file system walks ([#19632](https://github.com/tailwindlabs/tailwindcss/pull/19632)) +- Ensure import aliases in Astro v5 work without crashing when using `@tailwindcss/vite` ([#19677](https://github.com/tailwindlabs/tailwindcss/issues/19677)) +- Allow escape characters in `@utility` names to improve support with formatters such as Biome ([#19626](https://github.com/tailwindlabs/tailwindcss/pull/19626)) +- Fix incorrect canonicalization results when canonicalizing multiple times ([#19675](https://github.com/tailwindlabs/tailwindcss/pull/19675)) +- Add `.jj` to default ignored content directories ([#19687](https://github.com/tailwindlabs/tailwindcss/pull/19687)) + +### Deprecated + +- Deprecate `start-*` and `end-*` utilities in favor of `inset-s-*` and `inset-e-*` utilities ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) + +## [4.1.18] - 2025-12-11 + +### Fixed + +- Ensure validation of `source(…)` happens relative to the file it is in ([#19274](https://github.com/tailwindlabs/tailwindcss/pull/19274)) +- Include filename and line numbers in CSS parse errors ([#19282](https://github.com/tailwindlabs/tailwindcss/pull/19282)) +- Skip comments in Ruby files when checking for class names ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243)) +- Skip over arbitrary property utilities with a top-level `!` in the value ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243)) +- Support environment API in `@tailwindcss/vite` ([#18970](https://github.com/tailwindlabs/tailwindcss/pull/18970)) +- Preserve case of theme keys from JS configs and plugins ([#19337](https://github.com/tailwindlabs/tailwindcss/pull/19337)) +- Write source maps correctly on the CLI when using `--watch` ([#19373](https://github.com/tailwindlabs/tailwindcss/pull/19373)) +- Handle special defaults (like `ringColor.DEFAULT`) in JS configs ([#19348](https://github.com/tailwindlabs/tailwindcss/pull/19348)) +- Improve backwards compatibility for `content` theme key from JS configs ([#19381](https://github.com/tailwindlabs/tailwindcss/pull/19381)) +- Upgrade: Handle `future` and `experimental` config keys ([#19344](https://github.com/tailwindlabs/tailwindcss/pull/19344)) +- Try to canonicalize any arbitrary utility to a bare value ([#19379](https://github.com/tailwindlabs/tailwindcss/pull/19379)) +- Validate candidates similarly to Oxide ([#19397](https://github.com/tailwindlabs/tailwindcss/pull/19397)) +- Canonicalization: combine `text-*` and `leading-*` classes ([#19396](https://github.com/tailwindlabs/tailwindcss/pull/19396)) +- Correctly handle duplicate CLI arguments ([#19416](https://github.com/tailwindlabs/tailwindcss/pull/19416)) +- Don’t emit color-mix fallback rules inside `@keyframes` ([#19419](https://github.com/tailwindlabs/tailwindcss/pull/19419)) +- CLI: Don't hang when output is `/dev/stdout` ([#19421](https://github.com/tailwindlabs/tailwindcss/pull/19421)) + +## [3.4.19] - 2025-12-10 + +### Fixed + +- Don’t break `sibling-*()` functions when used inside `calc(…)` ([#19335](https://github.com/tailwindlabs/tailwindcss/pull/19335)) + +## [4.1.17] - 2025-11-06 + +### Fixed + +- Substitute `@variant` inside legacy JS APIs ([#19263](https://github.com/tailwindlabs/tailwindcss/pull/19263)) +- Prevent occasional crash on Windows when loaded into a worker thread ([#19242](https://github.com/tailwindlabs/tailwindcss/pull/19242)) + +## [4.1.16] - 2025-10-23 + +### Fixed + +- Discard candidates with an empty data type ([#19172](https://github.com/tailwindlabs/tailwindcss/pull/19172)) +- Fix canonicalization of arbitrary variants with attribute selectors ([#19176](https://github.com/tailwindlabs/tailwindcss/pull/19176)) +- Fix invalid colors due to nested `&` ([#19184](https://github.com/tailwindlabs/tailwindcss/pull/19184)) +- Improve canonicalization for `& > :pseudo` and `& :pseudo` arbitrary variants ([#19178](https://github.com/tailwindlabs/tailwindcss/pull/19178)) + +## [4.1.15] - 2025-10-20 + +### Fixed + +- Fix Safari devtools rendering issue due to `color-mix` fallback ([#19069](https://github.com/tailwindlabs/tailwindcss/pull/19069)) +- Suppress Lightning CSS warnings about `:deep`, `:slotted`, and `:global` ([#19094](https://github.com/tailwindlabs/tailwindcss/pull/19094)) +- Fix resolving theme keys when starting with the name of another theme key in JS configs and plugins ([#19097](https://github.com/tailwindlabs/tailwindcss/pull/19097)) +- Allow named groups in combination with `not-*`, `has-*`, and `in-*` ([#19100](https://github.com/tailwindlabs/tailwindcss/pull/19100)) +- Prevent important utilities from affecting other utilities ([#19110](https://github.com/tailwindlabs/tailwindcss/pull/19110)) +- Don’t index into strings with the `theme(…)` function ([#19111](https://github.com/tailwindlabs/tailwindcss/pull/19111)) +- Fix parsing issue when `\t` is used in at-rules ([#19130](https://github.com/tailwindlabs/tailwindcss/pull/19130)) +- Upgrade: Canonicalize utilities containing `0` values ([#19095](https://github.com/tailwindlabs/tailwindcss/pull/19095)) +- Upgrade: Migrate deprecated `break-words` to `wrap-break-word` ([#19157](https://github.com/tailwindlabs/tailwindcss/pull/19157)) + +### Changed + +- Remove the `postinstall` script from oxide ([#19149])(https://github.com/tailwindlabs/tailwindcss/pull/19149) + +## [4.1.14] - 2025-10-01 + +### Fixed + +- Handle `'` syntax in ClojureScript when extracting classes ([#18888](https://github.com/tailwindlabs/tailwindcss/pull/18888)) +- Handle `@variant` inside `@custom-variant` ([#18885](https://github.com/tailwindlabs/tailwindcss/pull/18885)) +- Merge suggestions when using `@utility` ([#18900](https://github.com/tailwindlabs/tailwindcss/pull/18900)) +- Ensure that file system watchers created when using the CLI are always cleaned up ([#18905](https://github.com/tailwindlabs/tailwindcss/pull/18905)) +- Do not generate `grid-column` utilities when configuring `grid-column-start` or `grid-column-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907)) +- Do not generate `grid-row` utilities when configuring `grid-row-start` or `grid-row-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907)) +- Prevent duplicate CSS when overwriting a static utility with a theme key ([#18056](https://github.com/tailwindlabs/tailwindcss/pull/18056)) +- Show Lightning CSS warnings (if any) when optimizing/minifying ([#18918](https://github.com/tailwindlabs/tailwindcss/pull/18918)) +- Use `default` export condition for `@tailwindcss/vite` ([#18948](https://github.com/tailwindlabs/tailwindcss/pull/18948)) +- Re-throw errors from PostCSS nodes ([#18373](https://github.com/tailwindlabs/tailwindcss/pull/18373)) +- Detect classes in markdown inline directives ([#18967](https://github.com/tailwindlabs/tailwindcss/pull/18967)) +- Ensure files with only `@theme` produce no output when built ([#18979](https://github.com/tailwindlabs/tailwindcss/pull/18979)) +- Support Maud templates when extracting classes ([#18988](https://github.com/tailwindlabs/tailwindcss/pull/18988)) +- Upgrade: Do not migrate `variant = 'outline'` during upgrades ([#18922](https://github.com/tailwindlabs/tailwindcss/pull/18922)) +- Upgrade: Show version mismatch (if any) when running upgrade tool ([#19028](https://github.com/tailwindlabs/tailwindcss/pull/19028)) +- Upgrade: Ensure first class inside `className` is migrated ([#19031](https://github.com/tailwindlabs/tailwindcss/pull/19031)) +- Upgrade: Migrate classes inside `*ClassName` and `*Class` attributes ([#19031](https://github.com/tailwindlabs/tailwindcss/pull/19031)) + +## [3.4.18] - 2025-10-01 + +### Fixed + +- Improve support for raw `supports-[…]` queries in arbitrary values ([#13605](https://github.com/tailwindlabs/tailwindcss/pull/13605)) +- Fix `require.cache` error when loaded through a TypeScript file in Node 22.18+ ([#18665](https://github.com/tailwindlabs/tailwindcss/pull/18665)) +- Support `import.meta.resolve(…)` in configs for new enough Node.js versions ([#18938](https://github.com/tailwindlabs/tailwindcss/pull/18938)) +- Allow using newer versions of `postcss-load-config` for better ESM and TypeScript PostCSS config support with the CLI ([#18938](https://github.com/tailwindlabs/tailwindcss/pull/18938)) +- Remove irrelevant utility rules when matching important classes ([#19030](https://github.com/tailwindlabs/tailwindcss/pull/19030)) + +## [4.1.13] - 2025-09-03 + +### Changed + +- Drop warning from browser build ([#18731](https://github.com/tailwindlabs/tailwindcss/issues/18731)) +- Drop exact duplicate declarations when emitting CSS ([#18809](https://github.com/tailwindlabs/tailwindcss/issues/18809)) + +### Fixed + +- Don't transition `visibility` when using `transition` ([#18795](https://github.com/tailwindlabs/tailwindcss/pull/18795)) +- Discard matched variants with unknown named values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799)) +- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799)) +- Show suggestions for known `matchVariant` values ([#18798](https://github.com/tailwindlabs/tailwindcss/pull/18798)) +- Replace deprecated `clip` with `clip-path` in `sr-only` ([#18769](https://github.com/tailwindlabs/tailwindcss/pull/18769)) +- Hide internal fields from completions in `matchUtilities` ([#18820](https://github.com/tailwindlabs/tailwindcss/pull/18820)) +- Ignore `.vercel` folders by default (can be overridden by `@source …` rules) ([#18855](https://github.com/tailwindlabs/tailwindcss/pull/18855)) +- Consider variants starting with `@-` to be invalid (e.g. `@-2xl:flex`) ([#18869](https://github.com/tailwindlabs/tailwindcss/pull/18869)) +- Do not allow custom variants to start or end with a `-` or `_` ([#18867](https://github.com/tailwindlabs/tailwindcss/pull/18867), [#18872](https://github.com/tailwindlabs/tailwindcss/pull/18872)) +- Upgrade: Migrate `aria` theme keys to `@custom-variant` ([#18815](https://github.com/tailwindlabs/tailwindcss/pull/18815)) +- Upgrade: Migrate `data` theme keys to `@custom-variant` ([#18816](https://github.com/tailwindlabs/tailwindcss/pull/18816)) +- Upgrade: Migrate `supports` theme keys to `@custom-variant` ([#18817](https://github.com/tailwindlabs/tailwindcss/pull/18817)) + +## [4.1.12] - 2025-08-13 + +### Fixed + +- Don't consider the global important state in `@apply` ([#18404](https://github.com/tailwindlabs/tailwindcss/pull/18404)) +- Add missing suggestions for `flex-` utilities ([#18642](https://github.com/tailwindlabs/tailwindcss/pull/18642)) +- Fix trailing `)` from interfering with extraction in Clojure keywords ([#18345](https://github.com/tailwindlabs/tailwindcss/pull/18345)) +- Detect classes inside Elixir charlist, word list, and string sigils ([#18432](https://github.com/tailwindlabs/tailwindcss/pull/18432)) +- Track source locations through `@plugin` and `@config` ([#18345](https://github.com/tailwindlabs/tailwindcss/pull/18345)) +- Allow boolean values of `process.env.DEBUG` in `@tailwindcss/node` ([#18485](https://github.com/tailwindlabs/tailwindcss/pull/18485)) +- Ignore consecutive semicolons in the CSS parser ([#18532](https://github.com/tailwindlabs/tailwindcss/pull/18532)) +- Center the dropdown icon added to an input with a paired datalist by default ([#18511](https://github.com/tailwindlabs/tailwindcss/pull/18511)) +- Extract candidates in Slang templates ([#18565](https://github.com/tailwindlabs/tailwindcss/pull/18565)) +- Improve error messages when encountering invalid functional utility names ([#18568](https://github.com/tailwindlabs/tailwindcss/pull/18568)) +- Discard CSS AST objects with `false` or `undefined` properties ([#18571](https://github.com/tailwindlabs/tailwindcss/pull/18571)) +- Allow users to disable URL rebasing in `@tailwindcss/postcss` via `transformAssetUrls: false` ([#18321](https://github.com/tailwindlabs/tailwindcss/pull/18321)) +- Fix false-positive migrations in `addEventListener` and JavaScript variable names ([#18718](https://github.com/tailwindlabs/tailwindcss/pull/18718)) +- Fix Standalone CLI showing default Bun help when run via symlink on Windows ([#18723](https://github.com/tailwindlabs/tailwindcss/pull/18723)) +- Read from `--border-color-*` theme keys in `divide-*` utilities for backwards compatibility ([#18704](https://github.com/tailwindlabs/tailwindcss/pull/18704/)) +- Don't scan `.hdr` and `.exr` files for classes by default ([#18734](https://github.com/tailwindlabs/tailwindcss/pull/18734)) + +## [4.1.11] - 2025-06-26 + +### Fixed + +- Add heuristic to skip candidate migrations inside `emit(…)` ([#18330](https://github.com/tailwindlabs/tailwindcss/pull/18330)) +- Extract candidates with variants in Clojure/ClojureScript keywords ([#18338](https://github.com/tailwindlabs/tailwindcss/pull/18338)) +- Document `--watch=always` in the CLI's usage ([#18337](https://github.com/tailwindlabs/tailwindcss/pull/18337)) +- Add support for Vite 7 to `@tailwindcss/vite` ([#18384](https://github.com/tailwindlabs/tailwindcss/pull/18384)) + +## [4.1.10] - 2025-06-11 + +### Fixed + +- Fix incorrectly generated CSS when using percentages in arbitrary values with calc (e.g. `w-[calc(100%-var(--offset))]`) ([#18289](https://github.com/tailwindlabs/tailwindcss/pull/18289)) + +## [4.1.9] - 2025-06-11 + +### Fixed + +- Correctly parse custom properties with strings containing semicolons ([#18251](https://github.com/tailwindlabs/tailwindcss/pull/18251)) +- Upgrade: Migrate arbitrary modifiers without percentage signs to bare values (e.g. `/[0.16]` → `/16`) ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184)) +- Upgrade: Migrate CSS variable shorthands where fallback value contains function call ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184)) +- Upgrade: Migrate negative arbitrary values to negative bare values (e.g. `mb-[-32rem]` → `-mb-128`) ([#18212](https://github.com/tailwindlabs/tailwindcss/pull/18212)) +- Upgrade: Do not migrate `blur` in `wire:model.blur` ([#18216](https://github.com/tailwindlabs/tailwindcss/pull/18216)) +- Don't add spaces around CSS dashed idents when formatting math expressions ([#18220](https://github.com/tailwindlabs/tailwindcss/pull/18220)) + +## [4.1.8] - 2025-05-27 + +### Added + +- Improve error messages when `@apply` fails ([#18059](https://github.com/tailwindlabs/tailwindcss/pull/18059)) + +### Fixed + +- Upgrade: Do not migrate declarations that look like candidates in ` + `, + 'src/input.css': css` + @import 'tailwindcss'; + + .foo { + flex-shrink: 1; + } + + .bar { + @apply !underline; + } + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('./src/**/*.{css,vue}')).toMatchInlineSnapshot(` + " + --- ./src/index.vue --- + + + + + --- ./src/input.css --- + @import 'tailwindcss'; + + .foo { + flex-shrink: 1; + } + + .bar { + @apply underline!; + } + " + `) + }, +) + +function withBOM(text: string): string { + return '\uFEFF' + text +} diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index f2643143a..cab0d3d62 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -1,6 +1,6 @@ import path from 'node:path' import { describe } from 'vitest' -import { css, html, json, test, ts } from '../utils' +import { css, html, json, test, ts, txt } from '../utils' test( `upgrade JS config files with flat theme values, darkMode, and content fields`, @@ -30,13 +30,23 @@ test( 400: '#f87171', 500: 'red', }, + superRed: '#ff0000', steel: 'rgb(70 130 180 / )', smoke: 'rgba(245, 245, 245, var(--smoke-alpha, ))', }, + ringColor: { + DEFAULT: '#c0ffee', + }, + opacity: { + superOpaque: '0.95', + }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], base: ['1rem', { lineHeight: '2rem' }], + lg: ['1.125rem', '2.5rem'], + xl: ['1.5rem', '3rem', 'invalid'], + '2xl': ['2rem'], }, width: { px: '1px', @@ -137,12 +147,14 @@ test( 'src/test.js': ts` export default { 'shouldNotMigrate': !border.test + '', + 'filter': 'drop-shadow(0 0 0.5rem #000)', } `, 'src/index.html': html` -
+
`, 'node_modules/my-external-lib/src/template.html': html`
@@ -152,21 +164,22 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.{css,js,html}')).toMatchInlineSnapshot(` " --- src/index.html ---
+ class="tracking-superWide leading-superLoose" + >
+
--- src/input.css --- @import 'tailwindcss'; @source '../node_modules/my-external-lib/**/*.{html}'; - @variant dark (&:where(.dark, .dark *)); + @custom-variant dark (&:where(.dark, .dark *)); @theme { --shadow-*: initial; @@ -177,9 +190,15 @@ test( --color-red-500: #ef4444; --color-red-600: #dc2626; + --color-superRed: #ff0000; --color-steel: rgb(70 130 180); --color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1)); + --default-ring-color: #c0ffee; + + --opacity-*: initial; + --opacity-superOpaque: 95%; + --text-*: initial; --text-xs: 0.75rem; --text-xs--line-height: 1rem; @@ -187,6 +206,11 @@ test( --text-sm--line-height: 1.5rem; --text-base: 1rem; --text-base--line-height: 2rem; + --text-lg: 1.125rem; + --text-lg--line-height: 2.5rem; + --text-xl: 1.5rem; + --text-xl--line-height: 3rem; + --text-2xl: 2rem; --width-*: initial; --width-0: 0%; @@ -237,17 +261,18 @@ test( --width-225px: 225px; --font-sans: Inter, system-ui, sans-serif; - --font-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, - 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-display: + Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; --radius-4xl: 2rem; --animate-spin-clockwise: spin-clockwise 1s linear infinite; --animate-spin-counterclockwise: spin-counterclockwise 1s linear infinite; - --tracking-super-wide: 0.25em; + --tracking-superWide: 0.25em; - --leading-super-loose: 3; + --leading-superLoose: 3; @keyframes spin-clockwise { 0% { @@ -268,7 +293,7 @@ test( } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -281,13 +306,14 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } --- src/test.js --- export default { 'shouldNotMigrate': !border.test + '', + 'filter': 'drop-shadow(0 0 0.5rem #000)', } " `) @@ -296,6 +322,105 @@ test( }, ) +test( + 'skips gitignored template files even when they are explicitly referenced in `content`', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + '.gitignore': txt` + node_modules + src/ignored + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + + module.exports = { + content: [ + './src/migrate-me.html', + './src/ignored/do-not-migrate-me.html', + './node_modules/my-external-lib/template.html', + ], + theme: {}, + plugins: [], + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + 'src/migrate-me.html': html` +
+ `, + 'src/ignored/do-not-migrate-me.html': html` +
+ `, + 'node_modules/my-external-lib/template.html': html` +
+ `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect( + await fs.dumpFiles( + '{src/**/*.{css,html},node_modules/my-external-lib/template.html,.gitignore}', + ), + ).toMatchInlineSnapshot(` + " + --- .gitignore --- + node_modules + src/ignored + + --- src/input.css --- + @import 'tailwindcss'; + + @source './ignored/do-not-migrate-me.html'; + @source '../node_modules/my-external-lib/template.html'; + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + + --- src/migrate-me.html --- +
+ + --- node_modules/my-external-lib/template.html --- +
+ + --- src/ignored/do-not-migrate-me.html --- +
+ " + `) + }, +) + test( 'upgrades JS config files with plugins', { @@ -315,6 +440,14 @@ test( import customPlugin from './custom-plugin' export default { + darkMode: [ + 'variant', + [ + '@media not print { .dark & }', + '@media not eink { .dark & }', + '&:where(.dark, .dark *)', + ], + ], plugins: [ typography, customPlugin({ @@ -351,7 +484,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -376,8 +509,24 @@ test( is-arr-mixed: null, true, false, 1234567, 1.35, 'foo', 'bar', 'true'; } + @custom-variant dark { + @media not print { + .dark & { + @slot; + } + } + @media not eink { + .dark & { + @slot; + } + } + &:where(.dark, .dark *) { + @slot; + } + } + /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -390,7 +539,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -437,7 +586,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.{css,ts}')).toMatchInlineSnapshot(` " @@ -445,21 +594,21 @@ test( @import 'tailwindcss'; @theme { - --color-gray-50: oklch(0.985 0 0); - --color-gray-100: oklch(0.97 0 0); - --color-gray-200: oklch(0.922 0 0); - --color-gray-300: oklch(0.87 0 0); - --color-gray-400: oklch(0.708 0 0); - --color-gray-500: oklch(0.556 0 0); - --color-gray-600: oklch(0.439 0 0); - --color-gray-700: oklch(0.371 0 0); - --color-gray-800: oklch(0.269 0 0); - --color-gray-900: oklch(0.205 0 0); - --color-gray-950: oklch(0.145 0 0); + --color-gray-50: oklch(98.5% 0 none); + --color-gray-100: oklch(97% 0 none); + --color-gray-200: oklch(92.2% 0 none); + --color-gray-300: oklch(87% 0 none); + --color-gray-400: oklch(70.8% 0 none); + --color-gray-500: oklch(55.6% 0 none); + --color-gray-600: oklch(43.9% 0 none); + --color-gray-700: oklch(37.1% 0 none); + --color-gray-800: oklch(26.9% 0 none); + --color-gray-900: oklch(20.5% 0 none); + --color-gray-950: oklch(14.5% 0 none); } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -472,7 +621,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -523,7 +672,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -533,7 +682,7 @@ test( @config '../tailwind.config.ts'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -546,7 +695,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -601,7 +750,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -611,7 +760,7 @@ test( @config '../tailwind.config.ts'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -624,7 +773,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -675,7 +824,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -685,7 +834,7 @@ test( @config '../tailwind.config.ts'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -698,7 +847,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -785,7 +934,7 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('project-{a,b}/**/*.{css,ts}')).toMatchInlineSnapshot(` " @@ -797,7 +946,7 @@ test( } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -810,7 +959,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } @@ -822,7 +971,7 @@ test( } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -835,7 +984,7 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -883,7 +1032,7 @@ test( }, }, async ({ root, exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade', { + await exec('pnpm exec upgrade', { cwd: path.join(root, 'frontend'), }) @@ -899,7 +1048,7 @@ test( } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -912,7 +1061,253 @@ test( ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + }, +) + +test( + 'migrate aria theme keys to custom variants', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + export default { + content: { + relative: true, + files: ['./src/**/*.html'], + }, + theme: { + extend: { + aria: { + // Built-in (not really, but visible because of intellisense) + busy: 'busy="true"', + + // Automatically handled by bare values + foo: 'foo="true"', + + // Quotes are optional in CSS for these kinds of attribute + // selectors + bar: 'bar=true', + + // Not automatically handled by bare values because names differ + baz: 'qux="true"', + + // Completely custom + asc: 'sort="ascending"', + desc: 'sort="descending"', + }, + }, + }, + } + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @custom-variant aria-baz (&[aria-qux="true"]); + @custom-variant aria-asc (&[aria-sort="ascending"]); + @custom-variant aria-desc (&[aria-sort="descending"]); + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + }, +) + +test( + 'migrate data theme keys to custom variants', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + export default { + content: { + relative: true, + files: ['./src/**/*.html'], + }, + theme: { + extend: { + data: { + // Automatically handled by bare values + foo: 'foo', + + // Not automatically handled by bare values because names differ + bar: 'baz', + + // Custom + checked: 'ui~="checked"', + }, + }, + }, + } + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @custom-variant data-bar (&[data-baz]); + @custom-variant data-checked (&[data-ui~="checked"]); + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + }, +) + +test( + 'migrate supports theme keys to custom variants', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + export default { + content: { + relative: true, + files: ['./src/**/*.html'], + }, + theme: { + extend: { + supports: { + // Automatically handled by bare values (using CSS variable as the value) + foo: 'foo: var(--foo)', // parentheses are optional + bar: '(bar: var(--bar))', + + // Not automatically handled by bare values because names differ + foo: 'bar: var(--foo)', // parentheses are optional + bar: '(qux: var(--bar))', + + // Custom + grid: 'display: grid', + }, + }, + }, + } + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @custom-variant supports-foo { + @supports (bar: var(--foo)) { + @slot; + } + } + @custom-variant supports-bar { + @supports ((qux: var(--bar))) { + @slot; + } + } + @custom-variant supports-grid { + @supports (display: grid) { + @slot; + } + } + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); } } " @@ -951,7 +1346,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -959,7 +1354,7 @@ describe('border compatibility', () => { @import 'tailwindcss'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -972,7 +1367,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -1013,7 +1408,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -1021,7 +1416,7 @@ describe('border compatibility', () => { @import 'tailwindcss'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1034,7 +1429,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: oklch(0.623 0.214 259.815); + border-color: oklch(62.3% 0.214 259.815); } } " @@ -1061,7 +1456,7 @@ describe('border compatibility', () => { theme: { extend: { borderColor: ({ colors }) => ({ - DEFAULT: 'currentColor', + DEFAULT: 'currentcolor', }), }, }, @@ -1075,7 +1470,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -1114,7 +1509,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -1125,7 +1520,7 @@ describe('border compatibility', () => { @import 'tailwindcss'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1138,7 +1533,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -1185,7 +1580,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` " @@ -1194,7 +1589,7 @@ describe('border compatibility', () => { @import 'tailwindcss/preflight' layer(base); /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1207,7 +1602,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } @@ -1288,18 +1683,18 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(` " --- src/index.html ---
--- src/input.css --- @@ -1312,7 +1707,7 @@ describe('border compatibility', () => { } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1325,15 +1720,15 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } .container { - width: calc(var(--spacing) * 2); - width: calc(var(--spacing) * 4.5); + width: --spacing(2); + width: --spacing(4.5); width: var(--spacing-5_5); - width: calc(var(--spacing) * 13); + width: --spacing(13); width: var(--spacing-100); width: var(--spacing-miami); } @@ -1398,18 +1793,18 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(` " --- src/index.html ---
--- src/input.css --- @@ -1426,7 +1821,7 @@ describe('border compatibility', () => { } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1439,7 +1834,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } @@ -1500,7 +1895,7 @@ describe('border compatibility', () => { }, }, async ({ exec, fs, expect }) => { - await exec('npx @tailwindcss/upgrade') + await exec('pnpm exec upgrade') expect(await fs.dumpFiles('src/**/*.{css,html}')).toMatchInlineSnapshot(` " @@ -1513,7 +1908,7 @@ describe('border compatibility', () => { @utility container { margin-inline: auto; padding-inline: 2rem; - @media (width >= theme(--breakpoint-sm)) { + @media (width >= --theme(--breakpoint-sm)) { max-width: none; } @media (width >= 48rem) { @@ -1529,7 +1924,7 @@ describe('border compatibility', () => { } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -1542,7 +1937,7 @@ describe('border compatibility', () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } " @@ -1550,3 +1945,243 @@ describe('border compatibility', () => { }, ) }) + +test( + `future and experimental keys are supported`, + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + import defaultTheme from 'tailwindcss/defaultTheme' + + module.exports = { + darkMode: 'selector', + content: ['./src/**/*.{html,js}'], + future: { + hoverOnlyWhenSupported: true, + respectDefaultRingColorOpacity: true, + disableColorOpacityUtilitiesByDefault: true, + relativeContentPathsByDefault: true, + }, + experimental: { + generalizedModifiers: true, + }, + theme: { + colors: { + red: { + 400: '#f87171', + 500: 'red', + }, + }, + }, + plugins: [], + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @custom-variant dark (&:where(.dark, .dark *)); + + @theme { + --color-*: initial; + --color-red-400: #f87171; + --color-red-500: red; + } + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + + expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toBe('') + }, +) + +test( + `unknown future keys dont migrate the config`, + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + import defaultTheme from 'tailwindcss/defaultTheme' + + module.exports = { + darkMode: 'selector', + content: ['./src/**/*.{html,js}'], + future: { + something: true, + }, + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @config '../tailwind.config.ts'; + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + + expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toMatchInlineSnapshot(` + "--- tailwind.config.ts --- + import { type Config } from 'tailwindcss' + import defaultTheme from 'tailwindcss/defaultTheme' + + module.exports = { + darkMode: 'selector', + content: ['./src/**/*.{html,js}'], + future: { + something: true, + }, + } satisfies Config" + `) + }, +) + +test( + `unknown experimental keys dont migrate the config`, + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + } + } + `, + 'tailwind.config.ts': ts` + import { type Config } from 'tailwindcss' + import defaultTheme from 'tailwindcss/defaultTheme' + + module.exports = { + darkMode: 'selector', + content: ['./src/**/*.{html,js}'], + experimental: { + something: true, + }, + } satisfies Config + `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm exec upgrade') + + expect(await fs.dumpFiles('src/**/*.css')).toMatchInlineSnapshot(` + " + --- src/input.css --- + @import 'tailwindcss'; + + @config '../tailwind.config.ts'; + + /* + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. + */ + @layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentcolor); + } + } + " + `) + + expect((await fs.dumpFiles('tailwind.config.ts')).trim()).toMatchInlineSnapshot(` + "--- tailwind.config.ts --- + import { type Config } from 'tailwindcss' + import defaultTheme from 'tailwindcss/defaultTheme' + + module.exports = { + darkMode: 'selector', + content: ['./src/**/*.{html,js}'], + experimental: { + something: true, + }, + } satisfies Config" + `) + }, +) diff --git a/integrations/upgrade/upgrade-errors.test.ts b/integrations/upgrade/upgrade-errors.test.ts new file mode 100644 index 000000000..fdc60828e --- /dev/null +++ b/integrations/upgrade/upgrade-errors.test.ts @@ -0,0 +1,254 @@ +import { stripVTControlCharacters } from 'node:util' +import { css, html, js, json, test } from '../utils' + +test( + 'upgrades half-upgraded v3 project to v4 (pnpm)', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + }, + "devDependencies": { + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'tailwind.config.js': js` + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: ['./src/**/*.{html,js}'], + } + `, + 'src/index.html': html` +
Hello World
+ `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + retry: 0, + }, + async ({ exec, expect }) => { + // Ensure we are in a git repo + await exec('git init') + await exec('git add --all') + await exec('git commit -m "before migration"') + + // Fully upgrade to v4 + await exec('pnpm exec upgrade') + + // Undo all changes to the current repo. This will bring the repo back to a + // v3 state, but the `node_modules` will now have v4 installed. + await exec('git reset --hard HEAD') + + // Re-running the upgrade should result in an error + return expect(() => { + return exec('pnpm exec upgrade', {}, { ignoreStdErr: true }).catch((e) => { + // Replacing the current version with a hardcoded `v4` to make it stable + // when we release new minor/patch versions. + return Promise.reject( + stripVTControlCharacters(e.message.replace(/\d+\.\d+\.\d+/g, '4.0.0')), + ) + }) + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Command failed: pnpm exec upgrade + ≈ tailwindcss v4.0.0 + + │ ↳ Upgrading from Tailwind CSS \`v4.0.0\` + + │ ↳ Version mismatch + │ + │ \`\`\`diff + │ - "tailwindcss": "^3" (expected version in \`package.json\`) + │ + "tailwindcss": "4.0.0" (installed version in \`node_modules\`) + │ \`\`\` + │ + │ Make sure to run \`pnpm install\` and try again. + + " + `) + }, +) + +test( + 'upgrades half-upgraded v3 project to v4 (bun)', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + }, + "devDependencies": { + "@tailwindcss/cli": "workspace:^", + "bun": "^1.0.0" + } + } + `, + 'tailwind.config.js': js` + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: ['./src/**/*.{html,js}'], + } + `, + 'src/index.html': html` +
Hello World
+ `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + retry: 0, + }, + async ({ exec, expect }) => { + // Use `bun` to install dependencies + await exec('rm ./pnpm-lock.yaml') + try { + await exec('npx bun install', {}, { ignoreStdErr: true }) + } catch (e) { + // When preparing for a release, the version in `package.json` will point + // to a non-existent version because it's not published yet. + // TODO: Find a better approach to handle this and actually test it even + // on release branches. Note: the pnpm version _does_ work because of + // overrides in the package.json file. + if (`${e}`.includes('No version matching')) return + throw e + } + + // Ensure we are in a git repo + await exec('git init') + await exec('git add --all') + await exec('git commit -m "before migration"') + + // Fully upgrade to v4 + await exec('npx @tailwindcss/upgrade') + + // Undo all changes to the current repo. This will bring the repo back to a + // v3 state, but the `node_modules` will now have v4 installed. + await exec('git reset --hard HEAD') + + // Re-running the upgrade should result in an error + return expect(() => { + return exec('npx @tailwindcss/upgrade', {}, { ignoreStdErr: true }).catch((e) => { + // Replacing the current version with a hardcoded `v4` to make it stable + // when we release new minor/patch versions. + return Promise.reject( + stripVTControlCharacters(e.message.replace(/\d+\.\d+\.\d+/g, '4.0.0')), + ) + }) + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Command failed: npx @tailwindcss/upgrade + ≈ tailwindcss v4.0.0 + + │ ↳ Upgrading from Tailwind CSS \`v4.0.0\` + + │ ↳ Version mismatch + │ + │ \`\`\`diff + │ - "tailwindcss": "^3" (expected version in \`package.json\`) + │ + "tailwindcss": "4.0.0" (installed version in \`node_modules\`) + │ \`\`\` + │ + │ Make sure to run \`bun install\` and try again. + + " + `) + }, +) + +test( + 'upgrades half-upgraded v3 project to v4 (npm)', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "^3", + "@tailwindcss/upgrade": "workspace:^" + }, + "devDependencies": { + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'tailwind.config.js': js` + /** @type {import('tailwindcss').Config} */ + module.exports = { + content: ['./src/**/*.{html,js}'], + } + `, + 'src/index.html': html` +
Hello World
+ `, + 'src/input.css': css` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + }, + retry: 0, + }, + async ({ exec, expect }) => { + // Use `npm` to install dependencies + await exec('rm ./pnpm-lock.yaml') + await exec('rm -rf ./node_modules') + try { + await exec('npm install', {}, { ignoreStdErr: true }) + } catch (e) { + // When preparing for a release, the version in `package.json` will point + // to a non-existent version because it's not published yet. + // TODO: Find a better approach to handle this and actually test it even + // on release branches. Note: the pnpm version _does_ work because of + // overrides in the package.json file. + if (`${e}`.includes('npm error code ETARGET')) return + throw e + } + + // Ensure we are in a git repo + await exec('git init') + await exec('git add --all') + await exec('git commit -m "before migration"') + + // Fully upgrade to v4 + await exec('npx @tailwindcss/upgrade') + + // Undo all changes to the current repo. This will bring the repo back to a + // v3 state, but the `node_modules` will now have v4 installed. + await exec('git reset --hard HEAD') + + // Re-running the upgrade should result in an error + return expect(() => { + return exec('npx @tailwindcss/upgrade', {}, { ignoreStdErr: true }).catch((e) => { + // Replacing the current version with a hardcoded `v4` to make it stable + // when we release new minor/patch versions. + return Promise.reject( + stripVTControlCharacters(e.message.replace(/\d+\.\d+\.\d+/g, '4.0.0')), + ) + }) + }).rejects.toThrowErrorMatchingInlineSnapshot(` + "Command failed: npx @tailwindcss/upgrade + ≈ tailwindcss v4.0.0 + + │ ↳ Upgrading from Tailwind CSS \`v4.0.0\` + + │ ↳ Version mismatch + │ + │ \`\`\`diff + │ - "tailwindcss": "^3" (expected version in \`package.json\`) + │ + "tailwindcss": "4.0.0" (installed version in \`node_modules\`) + │ \`\`\` + │ + │ Make sure to run \`npm install\` and try again. + + " + `) + }, +) diff --git a/integrations/utils.ts b/integrations/utils.ts index 8f916a3d0..9863918d1 100644 --- a/integrations/utils.ts +++ b/integrations/utils.ts @@ -1,19 +1,28 @@ import dedent from 'dedent' import fastGlob from 'fast-glob' -import { exec, spawn } from 'node:child_process' +import { exec, execFile, spawn, type ChildProcess } from 'node:child_process' import fs from 'node:fs/promises' +import { createServer } from 'node:net' import { platform, tmpdir } from 'node:os' import path from 'node:path' -import { stripVTControlCharacters } from 'node:util' +import { promisify, stripVTControlCharacters } from 'node:util' +import { RawSourceMap, SourceMapConsumer } from 'source-map-js' import { test as defaultTest, type ExpectStatic } from 'vitest' +import * as Yaml from 'yaml' +import { createLineTable } from '../packages/tailwindcss/src/source-maps/line-table' +import { escape } from '../packages/tailwindcss/src/utils/escape' const REPO_ROOT = path.join(__dirname, '..') +const ROOT_PNPM_WORKSPACE = Yaml.parse( + await fs.readFile(path.join(REPO_ROOT, 'pnpm-workspace.yaml'), 'utf8'), +) const PUBLIC_PACKAGES = (await fs.readdir(path.join(REPO_ROOT, 'dist'))).map((name) => name.replace('tailwindcss-', '@tailwindcss/').replace('.tgz', ''), ) interface SpawnedProcess { - dispose: () => void + dispose: () => Promise + flush: () => void onStdout: (predicate: (message: string) => boolean) => Promise onStderr: (predicate: (message: string) => boolean) => Promise } @@ -25,29 +34,37 @@ interface ChildProcessOptions { interface ExecOptions { ignoreStdErr?: boolean + stdin?: string } interface TestConfig { fs: { [filePath: string]: string | Uint8Array } + + timeout?: number + installDependencies?: boolean + retry?: number } interface TestContext { root: string expect: ExpectStatic exec(command: string, options?: ChildProcessOptions, execOptions?: ExecOptions): Promise spawn(command: string, options?: ChildProcessOptions): Promise + parseSourceMap(opts: string | SourceMapOptions): SourceMap fs: { - write(filePath: string, content: string): Promise + write(filePath: string, content: string, encoding?: BufferEncoding): Promise + symlink(dst: string, src: string): Promise create(filePaths: string[]): Promise read(filePath: string): Promise + delete(filePath: string): Promise glob(pattern: string): Promise<[string, string][]> dumpFiles(pattern: string): Promise expectFileToContain( filePath: string, contents: string | RegExp | (string | RegExp)[], ): Promise - expectFileNotToContain(filePath: string, contents: string | string[]): Promise + expectFileNotToContain(filePath: string, contents: string[]): Promise } } type TestCallback = (context: TestContext) => Promise | void @@ -55,11 +72,14 @@ interface TestFlags { only?: boolean skip?: boolean debug?: boolean + concurrent?: boolean } type SpawnActor = { predicate: (message: string) => boolean; resolve: () => void } -const IS_WINDOWS = platform() === 'win32' +export const IS_WINDOWS = platform() === 'win32' + +const execFileAsync = promisify(execFile) const TEST_TIMEOUT = IS_WINDOWS ? 120000 : 60000 const ASSERTION_TIMEOUT = IS_WINDOWS ? 10000 : 5000 @@ -73,16 +93,16 @@ export function test( name: string, config: TestConfig, testCallback: TestCallback, - { only = false, skip = false, debug = false }: TestFlags = {}, + { only = false, skip = false, debug = false, concurrent = false }: TestFlags = {}, ) { return defaultTest( name, { - timeout: TEST_TIMEOUT, - retry: process.env.CI ? 2 : 0, + timeout: config.timeout ?? TEST_TIMEOUT, + retry: config.retry ?? (process.env.CI ? 2 : 0), only: only || (!process.env.CI && debug), skip, - concurrent: true, + concurrent, }, async (options) => { let rootDir = debug ? path.join(REPO_ROOT, '.debug') : TMP_ROOT @@ -99,12 +119,46 @@ export function test( let context = { root, expect: options.expect, + parseSourceMap, async exec( command: string, childProcessOptions: ChildProcessOptions = {}, execOptions: ExecOptions = {}, ) { let cwd = childProcessOptions.cwd ?? root + let originalCommand = command + + // Avoid `pnpm exec upgrade` on Windows because the upgrader runs + // `pnpm add`, which rewrites Windows `.cmd` shims while the current + // shim is still executing. + // + // Pretty sure this is a pnpm v11 bug on Windows. + if (IS_WINDOWS && command.includes('pnpm exec upgrade')) { + for (let base = cwd; ; base = path.dirname(base)) { + let upgradeBin = path.join( + base, + 'node_modules', + '@tailwindcss', + 'upgrade', + 'dist', + 'index.mjs', + ) + + try { + await fs.access(upgradeBin) + command = command.replace('pnpm exec upgrade', `node "${upgradeBin}"`) + break + } catch (error: any) { + if (error?.code !== 'ENOENT') throw error + } + + let parent = path.dirname(base) + if (parent === base) { + throw new Error(`Unable to resolve @tailwindcss/upgrade from ${cwd}`) + } + } + } + if (debug && cwd !== root) { let relative = path.relative(root, cwd) if (relative[0] !== '.') relative = `./${relative}` @@ -112,15 +166,23 @@ export function test( } if (debug) console.log(`> ${command}`) return new Promise((resolve, reject) => { - exec( + let child = exec( command, { cwd, ...childProcessOptions, - env: childProcessOptions.env, + env: { + ...process.env, + ...childProcessOptions.env, + }, }, (error, stdout, stderr) => { if (error) { + if (command !== originalCommand) { + error.message = error.message.replace(command, originalCommand) + let mappedError = error as any + mappedError.cmd = originalCommand + } if (execOptions.ignoreStdErr !== true) console.error(stderr) if (only || debug) { console.error(stdout) @@ -134,6 +196,10 @@ export function test( } }, ) + if (execOptions.stdin) { + child.stdin?.write(execOptions.stdin) + child.stdin?.end() + } }) }, async spawn(command: string, childProcessOptions: ChildProcessOptions = {}) { @@ -153,6 +219,7 @@ export function test( if (debug) console.log(`>& ${command}`) let child = spawn(command, { cwd, + detached: !IS_WINDOWS, shell: true, ...childProcessOptions, env: { @@ -161,22 +228,26 @@ export function test( }, }) - function dispose() { - if (!child.kill()) { - child.kill('SIGKILL') - } + let disposed = false - let timer = setTimeout( - () => - rejectDisposal?.(new Error(`spawned process (${command}) did not exit in time`)), - ASSERTION_TIMEOUT, + async function dispose() { + if (disposed) return disposePromise + disposed = true + + await killProcessTree(child) + + let timer = setTimeout(() => { + forceKillProcessTree(child) + rejectDisposal?.(new Error(`spawned process (${command}) did not exit in time`)) + }, ASSERTION_TIMEOUT) + disposePromise.then( + () => clearTimeout(timer), + () => clearTimeout(timer), ) - disposePromise.finally(() => { - clearTimeout(timer) - }) return disposePromise } disposables.push(dispose) + function onExit() { resolveDisposal?.() } @@ -245,6 +316,13 @@ export function test( return { dispose, + flush() { + stdoutActors.splice(0) + stderrActors.splice(0) + + stdoutMessages.splice(0) + stderrMessages.splice(0) + }, onStdout(predicate: (message: string) => boolean) { return new Promise((resolve) => { stdoutActors.push({ predicate, resolve }) @@ -260,7 +338,7 @@ export function test( } }, fs: { - async write(filename: string, content: string | Uint8Array): Promise { + async write(filename, content, encoding = 'utf8') { let full = path.join(root, filename) let dir = path.dirname(full) await fs.mkdir(dir, { recursive: true }) @@ -271,6 +349,8 @@ export function test( if (filename.endsWith('package.json')) { content = await overwriteVersionsInPackageJson(content) + } else if (filename.endsWith('pnpm-workspace.yaml')) { + content = overwriteVersionsInPnpmWorkspace(content) } // Ensure that files written on Windows use \r\n line ending @@ -278,10 +358,22 @@ export function test( content = content.replace(/\n/g, '\r\n') } - await fs.writeFile(full, content, 'utf-8') + await fs.writeFile(full, content, encoding) }, - async create(filenames: string[]): Promise { + async symlink(target, src) { + let targetAbsolute = path.join(root, target) + let targetParent = path.dirname(targetAbsolute) + await fs.mkdir(targetParent, { recursive: true }) + + let srcAbsolute = path.join(root, src) + let srcParent = path.dirname(srcAbsolute) + await fs.mkdir(srcParent, { recursive: true }) + + await fs.symlink(targetAbsolute, srcAbsolute) + }, + + async create(filenames) { for (let filename of filenames) { let full = path.join(root, filename) @@ -291,7 +383,11 @@ export function test( } }, - async read(filePath: string) { + async delete(filename) { + await fs.unlink(path.join(root, filename)) + }, + + async read(filePath) { let content = await fs.readFile(path.resolve(root, filePath), 'utf8') // Ensure that files read on Windows have \r\n line endings removed @@ -301,7 +397,7 @@ export function test( return content }, - async glob(pattern: string) { + async glob(pattern) { let files = await fastGlob(pattern, { cwd: root }) return Promise.all( files.map(async (file) => { @@ -309,12 +405,12 @@ export function test( return [ file, // Drop license comment - content.replace(/[\s\n]*\/\*! tailwindcss .*? \*\/[\s\n]*/g, ''), + content.replace(/[\s\n]*\/\*![\s\S]*?\*\/[\s\n]*/g, ''), ] }), ) }, - async dumpFiles(pattern: string) { + async dumpFiles(pattern) { let files = await context.fs.glob(pattern) return `\n${files .slice() @@ -373,18 +469,45 @@ export function test( node_modules/ ` + // Disable Git's automatic line ending conversion in these throwaway test + // repositories. Tools like pnpm write files (e.g. `pnpm-lock.yaml`) with + // LF endings, which on Windows (where `core.autocrlf=true` is common) + // makes Git emit noisy "LF will be replaced by CRLF" warnings when it + // touches those files. + config.fs['.gitattributes'] ??= txt` + * -text + ` + + // Ensure there is always a root `pnpm-workspace.yaml` so that transitive + // dependency overrides (and the build allow list) are injected into it. + // As of pnpm v10, these can no longer live in the `pnpm.overrides` field + // of `package.json`. + config.fs['pnpm-workspace.yaml'] ??= '' + for (let [filename, content] of Object.entries(config.fs)) { - await context.fs.write(filename, content) + if (content.toString().startsWith('symlink:')) { + // The symlink path is relative to the target destination's path + let target = path.join( + filename, + content.toString().slice('symlink:'.length), // Relative path + ) + + await context.fs.symlink(target, filename) + } else { + await context.fs.write(filename, content) + } } + let shouldInstallDependencies = config.installDependencies ?? true + try { - // In debug mode, the directory is going to be inside the pnpm workspace - // of the tailwindcss package. This means that `pnpm install` will run - // pnpm install on the workspace instead (expect if the root dir defines - // a separate workspace). We work around this by using the - // `--ignore-workspace` flag. - let ignoreWorkspace = debug && !config.fs['pnpm-workspace.yaml'] - await context.exec(`pnpm install${ignoreWorkspace ? ' --ignore-workspace' : ''}`) + // Every test project gets its own root `pnpm-workspace.yaml` (see + // above). This makes the test directory its own workspace root, so even + // in debug mode — where the directory lives inside this repository's + // pnpm workspace — `pnpm install` won't attach to the parent workspace. + if (shouldInstallDependencies) { + await context.exec(`pnpm install`) + } } catch (error: any) { console.error(error) console.error(error.stdout?.toString()) @@ -395,11 +518,18 @@ export function test( let disposables: (() => Promise)[] = [] async function dispose() { - await Promise.all(disposables.map((dispose) => dispose())) + let results = await Promise.allSettled(disposables.map((dispose) => dispose())) if (!debug) { await gracefullyRemove(root) } + + let errors = results.flatMap((result) => + result.status === 'rejected' ? [result.reason] : [], + ) + if (errors.length > 0) { + throw new AggregateError(errors, 'Failed to clean up spawned processes') + } } options.onTestFinished(dispose) @@ -409,7 +539,7 @@ export function test( try { await context.exec('git init', { cwd: root }) await context.exec('git add --all', { cwd: root }) - await context.exec('git commit -m "before migration"', { cwd: root }) + await context.exec('git commit -m "before migration" --allow-empty', { cwd: root }) } catch (error: any) { console.error(error) console.error(error.stdout?.toString()) @@ -428,6 +558,9 @@ test.only = (name: string, config: TestConfig, testCallback: TestCallback) => { test.skip = (name: string, config: TestConfig, testCallback: TestCallback) => { return test(name, config, testCallback, { skip: true }) } +test.concurrent = (name: string, config: TestConfig, testCallback: TestCallback) => { + return test(name, config, testCallback, { concurrent: true }) +} test.debug = (name: string, config: TestConfig, testCallback: TestCallback) => { return test(name, config, testCallback, { debug: true }) } @@ -451,27 +584,43 @@ async function overwriteVersionsInPackageJson(content: string): Promise } } + return JSON.stringify(json, null, 2) +} + +function overwriteVersionsInPnpmWorkspace(content: string): string { + let workspace = content.trim() === '' ? {} : Yaml.parse(content) + + // Inherit information from the root workspace + workspace.allowBuilds = { + ...ROOT_PNPM_WORKSPACE.allowBuilds, + ...workspace.allowBuilds, + } + // Inject transitive dependency overwrite. This is necessary because // @tailwindcss/vite internally depends on a specific version of // @tailwindcss/oxide and we instead want to resolve it to the locally built // version. - json.pnpm ||= {} - json.pnpm.overrides ||= {} + // + // As of pnpm v10, the `pnpm.overrides` field in `package.json` is no longer + // read. Overrides have to be defined in `pnpm-workspace.yaml` instead. + workspace.overrides ||= {} for (let pkg of PUBLIC_PACKAGES) { if (pkg === 'tailwindcss') { // We want to be explicit about the `tailwindcss` package so our tests can // also import v3 without conflicting v4 tarballs. - json.pnpm.overrides['@tailwindcss/node>tailwindcss'] = resolveVersion(pkg) - json.pnpm.overrides['@tailwindcss/upgrade>tailwindcss'] = resolveVersion(pkg) - json.pnpm.overrides['@tailwindcss/cli>tailwindcss'] = resolveVersion(pkg) - json.pnpm.overrides['@tailwindcss/postcss>tailwindcss'] = resolveVersion(pkg) - json.pnpm.overrides['@tailwindcss/vite>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/node>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/upgrade>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/cli>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/postcss>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/turbopack>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/vite>tailwindcss'] = resolveVersion(pkg) + workspace.overrides['@tailwindcss/webpack>tailwindcss'] = resolveVersion(pkg) } else { - json.pnpm.overrides[pkg] = resolveVersion(pkg) + workspace.overrides[pkg] = resolveVersion(pkg) } } - return JSON.stringify(json, null, 2) + return Yaml.stringify(workspace) } function resolveVersion(dependency: string) { @@ -488,6 +637,7 @@ export let css = dedent export let html = dedent export let ts = dedent export let js = dedent +export let jsx = dedent export let json = dedent export let yaml = dedent export let txt = dedent @@ -510,80 +660,6 @@ export function candidate(strings: TemplateStringsArray, ...values: any[]) { return `.${escape(output.join('').trim())}` } -// https://drafts.csswg.org/cssom/#serialize-an-identifier -export function escape(value: string) { - if (arguments.length == 0) { - throw new TypeError('`CSS.escape` requires an argument.') - } - var string = String(value) - var length = string.length - var index = -1 - var codeUnit - var result = '' - var firstCodeUnit = string.charCodeAt(0) - - if ( - // If the character is the first character and is a `-` (U+002D), and - // there is no second character, […] - length == 1 && - firstCodeUnit == 0x002d - ) { - return '\\' + string - } - - while (++index < length) { - codeUnit = string.charCodeAt(index) - // Note: there’s no need to special-case astral symbols, surrogate - // pairs, or lone surrogates. - - // If the character is NULL (U+0000), then the REPLACEMENT CHARACTER - // (U+FFFD). - if (codeUnit == 0x0000) { - result += '\uFFFD' - continue - } - - if ( - // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is - // U+007F, […] - (codeUnit >= 0x0001 && codeUnit <= 0x001f) || - codeUnit == 0x007f || - // If the character is the first character and is in the range [0-9] - // (U+0030 to U+0039), […] - (index == 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || - // If the character is the second character and is in the range [0-9] - // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] - (index == 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && firstCodeUnit == 0x002d) - ) { - // https://drafts.csswg.org/cssom/#escape-a-character-as-code-point - result += '\\' + codeUnit.toString(16) + ' ' - continue - } - - // If the character is not handled by one of the above rules and is - // greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or - // is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to - // U+005A), or [a-z] (U+0061 to U+007A), […] - if ( - codeUnit >= 0x0080 || - codeUnit == 0x002d || - codeUnit == 0x005f || - (codeUnit >= 0x0030 && codeUnit <= 0x0039) || - (codeUnit >= 0x0041 && codeUnit <= 0x005a) || - (codeUnit >= 0x0061 && codeUnit <= 0x007a) - ) { - // the character itself - result += string.charAt(index) - continue - } - - // Otherwise, the escaped character. - // https://drafts.csswg.org/cssom/#escape-a-character - result += '\\' + string.charAt(index) - } - return result -} - export async function retryAssertion( fn: () => Promise, { timeout = ASSERTION_TIMEOUT, delay = 5 }: { timeout?: number; delay?: number } = {}, @@ -594,6 +670,7 @@ export async function retryAssertion( try { return await fn() } catch (err) { + Error.captureStackTrace(err, retryAssertion) error = err await new Promise((resolve) => setTimeout(resolve, delay)) } @@ -609,7 +686,7 @@ export async function fetchStyles(base: string, path = '/'): Promise { let index = await fetch(`${base}${path}`) let html = await index.text() - let linkRegex = /]*>([\s\S]*?)<\/style>/gi let stylesheets: string[] = [] @@ -640,13 +717,155 @@ export async function fetchStyles(base: string, path = '/'): Promise { } return stylesheets.reduce((acc, css) => { - return acc + '\n' + css + if (acc.length > 0) acc += '\n' + acc += css + return acc }, '') } +export async function getRandomPort() { + return new Promise((resolve, reject) => { + let server = createServer() + server.unref() + server.on('error', reject) + server.listen(0, '127.0.0.1', () => { + let address = server.address() + server.close(() => { + if (address && typeof address === 'object') { + resolve(address.port) + } else { + reject(new Error('Unable to allocate random port')) + } + }) + }) + }) +} + +async function killProcessTree(child: ChildProcess) { + if (child.exitCode !== null || child.signalCode !== null || child.pid === undefined) { + return + } + + if (IS_WINDOWS) { + await execFileAsync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { + timeout: ASSERTION_TIMEOUT, + windowsHide: true, + }).catch(() => {}) + return + } + + try { + process.kill(-child.pid, 'SIGTERM') + } catch (error: any) { + if (error?.code !== 'ESRCH') { + child.kill() + } + } +} + +function forceKillProcessTree(child: ChildProcess) { + if (child.exitCode !== null || child.signalCode !== null || child.pid === undefined) { + return + } + + if (IS_WINDOWS) { + execFile('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true }, () => {}) + return + } + + try { + process.kill(-child.pid, 'SIGKILL') + } catch (error: any) { + if (error?.code !== 'ESRCH') { + child.kill('SIGKILL') + } + } +} + async function gracefullyRemove(dir: string) { // Skip removing the directory in CI because it can stall on Windows if (!process.env.CI) { - await fs.rm(dir, { recursive: true, force: true }) + await fs.rm(dir, { recursive: true, force: true }).catch((error) => { + console.log(`Failed to remove ${dir}`, error) + }) + } +} + +const SOURCE_MAP_COMMENT = /^\/\*# sourceMappingURL=data:application\/json;base64,(.*) \*\/$/ + +export interface SourceMap { + at( + line: number, + column: number, + ): { + source: string | null + original: string + generated: string + } +} + +interface SourceMapOptions { + /** + * A raw source map + * + * This may be a string or an object. Strings will be decoded. + */ + map: string | object + + /** + * The content of the generated file the source map is for + */ + content: string + + /** + * The encoding of the source map + * + * Can be used to decode a base64 map (e.g. an inline source map URI) + */ + encoding?: BufferEncoding +} + +function parseSourceMap(opts: string | SourceMapOptions): SourceMap { + if (typeof opts === 'string') { + let lines = opts.trimEnd().split('\n') + let comment = lines.at(-1) ?? '' + let map = String(comment).match(SOURCE_MAP_COMMENT)?.[1] ?? null + if (!map) throw new Error('No source map comment found') + + return parseSourceMap({ + map, + content: lines.slice(0, -1).join('\n'), + encoding: 'base64', + }) + } + + let rawMap: RawSourceMap + let content = opts.content + + if (typeof opts.map === 'object') { + rawMap = opts.map as RawSourceMap + } else { + rawMap = JSON.parse(Buffer.from(opts.map, opts.encoding ?? 'utf-8').toString()) + } + + let map = new SourceMapConsumer(rawMap) + let generatedTable = createLineTable(content) + + return { + at(line: number, column: number) { + let pos = map.originalPositionFor({ line, column }) + let source = pos.source ? map.sourceContentFor(pos.source) : null + let originalTable = createLineTable(source ?? '') + let originalOffset = originalTable.findOffset(pos) + let generatedOffset = generatedTable.findOffset({ line, column }) + + return { + source: pos.source, + original: source + ? source.slice(originalOffset, originalOffset + 10).trim() + '...' + : '(none)', + generated: content.slice(generatedOffset, generatedOffset + 10).trim() + '...', + } + }, } } diff --git a/integrations/vite/astro.test.ts b/integrations/vite/astro.test.ts index 5b7407e47..f828b5071 100644 --- a/integrations/vite/astro.test.ts +++ b/integrations/vite/astro.test.ts @@ -1,4 +1,4 @@ -import { candidate, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' +import { candidate, css, fetchStyles, html, js, json, retryAssertion, test, ts } from '../utils' test( 'dev mode', @@ -20,9 +20,8 @@ test( // https://astro.build/config export default defineConfig({ - vite: { - plugins: [tailwindcss()], - }, + vite: { plugins: [tailwindcss()] }, + build: { inlineStylesheets: 'never' }, }) `, 'src/pages/index.astro': html` @@ -70,3 +69,133 @@ test( }) }, ) + +test( + 'build mode', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "astro": "^4.15.2", + "react": "^19", + "react-dom": "^19", + "@astrojs/react": "^4", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + } + } + `, + 'astro.config.mjs': ts` + import tailwindcss from '@tailwindcss/vite' + import react from '@astrojs/react' + import { defineConfig } from 'astro/config' + + // https://astro.build/config + export default defineConfig({ + vite: { plugins: [tailwindcss()] }, + integrations: [react()], + build: { inlineStylesheets: 'never' }, + }) + `, + // prettier-ignore + 'src/pages/index.astro': html` + --- + import ClientOnly from './client-only'; + --- + +
Hello, world!
+ + + + + `, + 'src/pages/client-only.jsx': js` + export default function ClientOnly() { + return
Hello, world!
+ } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm astro build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + + await fs.expectFileToContain(files[0][0], [candidate`underline`, candidate`overline`]) + }, +) + +// https://github.com/tailwindlabs/tailwindcss/issues/19677 +test( + 'import aliases should work in + + +

Astro

+ + + `, + 'src/styles/global.css': css`@import 'tailwindcss';`, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm astro build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + + await fs.expectFileToContain(files[0][0], [candidate`underline`]) + }, +) diff --git a/integrations/vite/bundled-dev.test.ts b/integrations/vite/bundled-dev.test.ts new file mode 100644 index 000000000..8ba1b6b73 --- /dev/null +++ b/integrations/vite/bundled-dev.test.ts @@ -0,0 +1,193 @@ +import path from 'node:path' +import { candidate, css, html, json, retryAssertion, test, ts, txt, yaml } from '../utils' + +// Vite's experimental `bundledDev` mode invokes the `hotUpdate` hook without a +// `server`, which used to crash the plugin on every file edit. +// +// - https://github.com/tailwindlabs/tailwindcss/issues/20378 +// - https://vite.dev/blog/announcing-vite8-1#experimental-bundled-dev-mode +test( + 'dev mode (experimental `bundledDev`)', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + `, + 'project-a/package.json': txt` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8.1" + } + } + `, + 'project-a/vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + experimental: { + bundledDev: true, + }, + plugins: [tailwindcss()], + }) + `, + 'project-a/index.html': html` + + + + +
Hello, world!
+ + `, + 'project-a/src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + @source '../../project-b/src/**/*.html'; + `, + 'project-b/src/index.html': html` +
+ `, + }, + }, + async ({ root, spawn, fs, expect }) => { + let process = await spawn('pnpm vite dev', { + cwd: path.join(root, 'project-a'), + env: { + // Some CI machines have an upstream rolldown bug where its file watcher + // never delivers a single event. The debug output is the only way to + // tell that apart from a real regression (see below). + DEBUG: 'vite:full-bundle-mode', + }, + }) + + // Any debug output beyond the "INITIAL:" startup lines proves rolldown's + // watcher delivered at least one file event. + let watcherAlive = false + + // `hotUpdate` errors don't kill the dev server, they are only printed to + // stderr. Track them explicitly so a crash fails the test even if the + // rebuild happens to succeed anyway. + let pluginErrors: string[] = [] + process.onStderr((message) => { + if (message.includes('vite:full-bundle-mode')) { + if (!message.includes('INITIAL:')) watcherAlive = true + } else if (message.includes('@tailwindcss/vite')) { + pluginErrors.push(message) + } + return false + }) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + // In `bundledDev` mode the stylesheet is not served separately. Instead the + // generated CSS is embedded in the bundled JS and injected at runtime, so + // extract the bundle from the served HTML. While the bundle is being built, + // Vite serves a temporary fallback page instead. + // + // The bundle can be split into multiple chunks (e.g. the HMR client runtime + // and the app itself), and the chunk containing the CSS is not always the + // first one, so fetch every referenced script and stylesheet. + async function fetchBundledStyles(): Promise { + let index = await fetch(`${url}/`) + let html = await index.text() + if (html.includes('__vite_is_fallback_page__')) { + throw new Error('Bundling still in progress') + } + + let sources = [ + ...html.matchAll(/]*\ssrc="([^"]+)"/g), + ...html.matchAll(/]*\srel="stylesheet"[^>]*\shref="([^"]+)"/g), + ].map((match) => match[1]) + if (sources.length === 0) throw new Error(`No scripts or stylesheets found in:\n\n${html}`) + + let contents = await Promise.all( + sources.map(async (src) => { + let response = await fetch(new URL(src, `${url}/`)) + return await response.text() + }), + ) + return contents.join('\n') + } + + await retryAssertion( + async () => { + let styles = await fetchBundledStyles() + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`flex`) + }, + { timeout: 10_000, delay: 100 }, + ) + + // Edit a module file and a manually `@source`d file. Without a connected + // HMR websocket client (this test only uses HTTP fetches), a file change + // does not trigger an eager rebuild — it only marks the bundle as stale, + // and the next fetch kicks off the rebuild. So poll with fetches without + // touching the files again: rewriting would re-mark the bundle stale right + // before each fetch and the rebuild could never be observed. + await fs.write( + 'project-a/index.html', + html` + + + + +
Hello, world!
+ + `, + ) + + await fs.write('project-b/src/index.html', html` +
+ `) + + try { + await retryAssertion( + async () => { + let styles = await fetchBundledStyles() + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`flex`) + expect(styles).toContain(candidate`m-2`) + expect(styles).toContain(candidate`font-bold`) + }, + { timeout: 15_000, delay: 100 }, + ) + } catch (error) { + // If rolldown's watcher delivered any file event, the updates were + // genuinely dropped somewhere along the way — a real failure. + if (watcherAlive) throw error + + // Otherwise the watcher never delivered a single event, so the updates + // can never be observed no matter how long we wait — an upstream + // rolldown bug on some CI machines, not a plugin regression. The crash + // regression this test guards is still covered: `hotUpdate` is driven + // by Vite's own (working) chokidar watcher and asserted via + // `pluginErrors` below. + // + // Skipping is only sound while the server still healthily serves the + // original bundle. If it stopped serving (crashed, or wedged on the + // fallback page), that's real breakage a silent watcher must not mask. + console.log(error) + let styles = await fetchBundledStyles() + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`flex`) + console.warn( + 'Skipping update assertions: rolldown’s file watcher delivered no events on this machine.', + ) + } + + expect(pluginErrors).toEqual([]) + }, +) diff --git a/integrations/vite/config.test.ts b/integrations/vite/config.test.ts index 3bf560e07..0ac68f5e9 100644 --- a/integrations/vite/config.test.ts +++ b/integrations/vite/config.test.ts @@ -12,7 +12,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -76,7 +76,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -140,7 +140,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -219,7 +219,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, diff --git a/integrations/vite/css-modules.test.ts b/integrations/vite/css-modules.test.ts index 668281cb4..a680ab9b7 100644 --- a/integrations/vite/css-modules.test.ts +++ b/integrations/vite/css-modules.test.ts @@ -14,8 +14,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, diff --git a/integrations/vite/html-style-blocks.test.ts b/integrations/vite/html-style-blocks.test.ts new file mode 100644 index 000000000..22bb91d78 --- /dev/null +++ b/integrations/vite/html-style-blocks.test.ts @@ -0,0 +1,58 @@ +import { html, json, test, ts } from '../utils' + +test( + 'transforms html style blocks', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "@tailwindcss/vite": "workspace:^", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + +
+ + + + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + expect(await fs.dumpFiles('dist/*.html')).toMatchInlineSnapshot(` + " + --- dist/index.html --- + + + +
+ + + + " + `) + }, +) diff --git a/integrations/vite/ignored-packages.test.ts b/integrations/vite/ignored-packages.test.ts new file mode 100644 index 000000000..1efa9dc61 --- /dev/null +++ b/integrations/vite/ignored-packages.test.ts @@ -0,0 +1,81 @@ +import { candidate, css, fetchStyles, html, js, retryAssertion, test, ts, txt } from '../utils' + +const WORKSPACE = { + fs: { + 'package.json': txt` + { + "type": "module", + "dependencies": { + "tailwind-merge": "^2", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + `, + 'src/index.js': js` + import { twMerge } from 'tailwind-merge' + + twMerge('underline') + + console.log('underline') + `, + 'src/index.css': css`@import 'tailwindcss/utilities' layer(utilities);`, + }, +} + +test( + 'does not scan tailwind-merge in production builds', + WORKSPACE, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [, content] = files[0] + + expect(content).toMatchInlineSnapshot(` + "@layer utilities { + .underline { + text-decoration-line: underline; + } + } + " + `) + }, +) + +test('does not scan tailwind-merge in dev builds', WORKSPACE, async ({ spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + + expect(styles).not.toContain(candidate`flex`) + }) +}) diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts index eebe11127..c346f8e37 100644 --- a/integrations/vite/index.test.ts +++ b/integrations/vite/index.test.ts @@ -7,6 +7,7 @@ import { html, js, json, + jsx, retryAssertion, test, ts, @@ -33,8 +34,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -62,7 +63,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { } `, 'project-a/src/index.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; @config '../tailwind.config.js'; @source '../../project-b/src/**/*.html'; @@ -110,8 +111,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -147,11 +148,17 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { } `, 'project-a/src/index.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; + @import './imported.css'; @config '../tailwind.config.js'; @source '../../project-b/src/**/*.html'; `, + 'project-a/src/imported.css': css` + .imported { + color: red; + } + `, 'project-b/src/index.html': html`
`, @@ -174,22 +181,12 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { return Boolean(url) }) - // Candidates are resolved lazily, so the first visit of index.html - // will only have candidates from this file. await retryAssertion(async () => { let styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`flex`) - expect(styles).not.toContain(candidate`font-bold`) - }) - - // Going to about.html will extend the candidate list to include - // candidates from about.html. - await retryAssertion(async () => { - let styles = await fetchStyles(url, '/about.html') - expect(styles).toContain(candidate`underline`) - expect(styles).toContain(candidate`flex`) expect(styles).toContain(candidate`font-bold`) + expect(styles).toContain(candidate`imported`) }) await retryAssertion(async () => { @@ -210,6 +207,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`flex`) expect(styles).toContain(candidate`font-bold`) + expect(styles).toContain(candidate`imported`) expect(styles).toContain(candidate`m-2`) }) @@ -227,6 +225,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`flex`) expect(styles).toContain(candidate`font-bold`) + expect(styles).toContain(candidate`imported`) expect(styles).toContain(candidate`m-2`) expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`) }) @@ -248,11 +247,157 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { let styles = await fetchStyles(url) expect(styles).toContain(candidate`red`) expect(styles).toContain(candidate`flex`) + expect(styles).toContain(candidate`imported`) expect(styles).toContain(candidate`m-2`) expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`) expect(styles).toContain(candidate`font-bold`) }) + + await retryAssertion(async () => { + // Trigger a partial rebuild for the next test + await fs.write( + 'project-a/index.html', + html` + + + + +
Hello, world!
+ + `, + ) + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`m-4`) + }) + + await retryAssertion(async () => { + // Changing an `@imported` CSS file after a partial rebuild also triggers the correct update + await fs.write( + 'project-a/src/imported.css', + css` + .imported-updated { + color: red; + } + `, + ) + + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`red`) + expect(styles).toContain(candidate`flex`) + expect(styles).toContain(candidate`m-2`) + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`[.changed_&]:content-['project-b/src/index.js']`) + expect(styles).toContain(candidate`font-bold`) + expect(styles).toContain(candidate`imported-updated`) + }) + }, + ) + + // https://github.com/tailwindlabs/tailwindcss/issues/20346 + test( + 'dev mode + symlinked `@source` files pick up changes to the real file', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + - packages/* + `, + 'project-a/package.json': txt` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "repro-package": "workspace:*", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" + } + } + `, + 'project-a/vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'project-a/index.html': html` + + + + +
Hello, world!
+ + `, + + // The `@source` points through the `node_modules` symlink that pnpm + // creates for the workspace package. Vite's file watcher ignores + // `node_modules` by default, so the symlinked path can not be watched. + // The real file (in `packages/repro-package`) has to be watched + // instead. + 'project-a/src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + @source '../node_modules/repro-package/source.html'; + `, + 'packages/repro-package/package.json': json` + { + "name": "repro-package", + "private": true, + "version": "1.0.0" + } + `, + 'packages/repro-package/source.html': html` +
+ Hello, world! +
+ `, + }, + }, + async ({ root, spawn, fs, expect }) => { + let process = await spawn('pnpm vite dev', { + cwd: path.join(root, 'project-a'), + }) + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`content-['v1']`) + }) + + await retryAssertion(async () => { + // Changes to the real file (behind the symlink) should be picked up + await fs.write( + 'packages/repro-package/source.html', + html` +
+ Hello, world! +
+ `, + ) + + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`content-['v1']`) + expect(styles).toContain(candidate`content-['v2']`) + }) }, ) @@ -274,8 +419,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -302,7 +447,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { } `, 'project-a/src/index.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; @import './custom-theme.css'; @config '../tailwind.config.js'; @@ -435,6 +580,352 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { }, ) + describe.each([['^6'], ['7.0.8'], ['7.1.12'], ['7.3.1'], ['8.0.0']])( + 'Using Vite %s', + (version) => { + test( + 'external source file changes update the CSS', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + `, + 'project-a/package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "${version}" + } + } + `, + 'project-a/vite.config.ts': ts` + import fs from 'node:fs' + import path from 'node:path' + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, + build: { cssMinify: false }, + plugins: [tailwindcss()], + logLevel: 'info', + }) + `, + 'project-a/index.html': html` + + + + + +
+ + + + `, + 'project-a/src/main.ts': jsx`import { classes } from './app'`, + 'project-a/src/app.ts': jsx`export let classes = "content-['project-a/src/app.ts']"`, + 'project-a/src/index.css': css` + @import 'tailwindcss'; + @source '../../project-b/**/*.php'; + `, + 'project-b/src/index.php': html` +
+ `, + }, + }, + async ({ root, spawn, fs, expect }) => { + let process = await spawn('pnpm vite dev --debug hmr', { + cwd: path.join(root, 'project-a'), + }) + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['project-b/src/index.php']`) + }) + + // Flush all messages so that we can be sure the next messages are + // from the file changes we're about to make + process.flush() + + // Changing an external .php file hot-updates the generated CSS + { + await fs.write( + 'project-b/src/index.php', + txt`
`, + ) + + // On Vite < 7.1, Vite itself hard-invalidates watched files that + // aren't part of the module graph and reloads the page. + // + // On newer versions nothing reloads the page: the CSS hot-updates + // through the regular pipeline because the changed file is a + // watch dependency of the CSS root. + // + // Reloading the page for external template changes is the + // responsibility of the backend integration (e.g. `laravel-vite-plugin`'s `refresh` option, or `vite-plugin-full-reload`). + // + // https://github.com/tailwindlabs/tailwindcss/issues/20411 + if (version === '^6' || version === '7.0.8') { + await process.onStdout((m) => m.includes('page reload') && m.includes('index.php')) + } else { + await process.onStdout((m) => m.includes('hmr update') && m.includes('index.css')) + } + + // Ensure the styles were regenerated with the new content + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['updated:project-b/src/index.php']`) + } + }, + ) + }, + ) + + // https://github.com/tailwindlabs/tailwindcss/issues/20320 + // https://github.com/tailwindlabs/tailwindcss/issues/19903 + test( + 'editing scanned files that Vite can process as modules does not trigger a full reload', + { + fs: { + 'package.json': json`{}`, + 'pnpm-workspace.yaml': yaml` + # + packages: + - project-a + `, + 'project-a/package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^8" + } + } + `, + 'project-a/vite.config.ts': ts` + import fs from 'node:fs' + import path from 'node:path' + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, + build: { cssMinify: false }, + plugins: [ + tailwindcss(), + { + // A plugin that processes a custom file type into a JS + // module, similar to e.g. \`.vue\` or \`.svelte\` files + name: 'custom-file-type', + transform(code, id) { + if (id.endsWith('.custom')) { + return { code: 'export default ' + JSON.stringify(code), map: null } + } + }, + }, + { + // Log all HMR payloads to a file so the test can assert on + // them + name: 'hmr-wiretap', + configureServer(server) { + let logFile = path.resolve('hmr.log') + fs.writeFileSync(logFile, '') + for (let environment of Object.values(server.environments)) { + let send = environment.hot.send.bind(environment.hot) + environment.hot.send = (payload) => { + fs.appendFileSync(logFile, JSON.stringify(payload) + '\\n') + return send(payload) + } + } + }, + }, + ], + }) + `, + 'project-a/index.html': html` + + + + + +
+ + + + `, + 'project-a/src/main.ts': ts` + import compA from './comp-a.custom' + import snippet from './snippet.php?raw' + console.log(compA, snippet) + `, + 'project-a/src/snippet.php': html` +
+ `, + 'project-a/src/comp-a.custom': html` +
+ `, + 'project-a/src/comp-b.custom': html` +
+ `, + 'project-a/src/lazy.tsx': jsx` + export default function Lazy() { + return
+ } + `, + 'project-a/src/unimported.css': css` + .unimported { + color: red; + } + `, + 'project-a/src/index.css': css` + @import 'tailwindcss'; + @source '../../project-b/**/*.php'; + `, + 'project-b/src/index.php': html` +
+ `, + }, + }, + async ({ root, spawn, fs, expect }) => { + let process = await spawn('pnpm vite dev', { + cwd: path.join(root, 'project-a'), + }) + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['src/lazy.tsx']`) + expect(styles).toContain(candidate`content-['src/comp-b.custom']`) + expect(styles).toContain(candidate`content-['project-b/src/index.php']`) + }) + + // Load `main.ts`, `comp-a.custom`, and `snippet.php?raw` as real + // modules, like a browser visiting the page would + await fetch(`${url}/src/main.ts`) + await fetch(`${url}/src/comp-a.custom?import`) + await fetch(`${url}/src/snippet.php?raw`) + + // Changing a scanned `.tsx` file that is not part of the module graph + // (e.g. a lazily-loaded route that hasn't been visited yet) should not + // trigger a full reload, but new classes should still apply + // + // https://github.com/tailwindlabs/tailwindcss/issues/20320 + { + await fs.write( + 'project-a/src/lazy.tsx', + jsx` + export default function Lazy() { + return
+ } + `, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['updated:src/lazy.tsx']`) + }) + } + + // The same holds for a custom file type as long as some file of the + // same type was processed as a module before + { + await fs.write( + 'project-a/src/comp-b.custom', + html`
`, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['updated:src/comp-b.custom']`) + }) + } + + // Changing a scanned stylesheet that is not part of the module graph + // (e.g. a component stylesheet that a framework plugin compiles into + // the component) should not trigger a full reload either + // + // https://github.com/tailwindlabs/tailwindcss/issues/19903 + { + let updates = (await fs.read('project-a/hmr.log')).split('"type":"update"').length + + await fs.write( + 'project-a/src/unimported.css', + css` + .unimported { + color: blue; + } + `, + ) + + // Wait until the change was handled and an update was pushed + await retryAssertion(async () => { + let log = await fs.read('project-a/hmr.log') + expect(log.split('"type":"update"').length).toBeGreaterThan(updates) + }) + } + + // Changing an external file (e.g. a PHP template) hot-updates the + // generated CSS but does not trigger a full reload either. Reloading the + // page for external template changes is the responsibility of the backend + // integration (e.g. `laravel-vite-plugin`'s `refresh` option, or + // `vite-plugin-full-reload`). + // + // https://github.com/tailwindlabs/tailwindcss/issues/20411 + { + let updates = (await fs.read('project-a/hmr.log')).split('"type":"update"').length + + await fs.write( + 'project-b/src/index.php', + html`
`, + ) + + await retryAssertion(async () => { + let log = await fs.read('project-a/hmr.log') + expect(log.split('"type":"update"').length).toBeGreaterThan(updates) + }) + + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['updated:project-b/src/index.php']`) + } + }, + ) + test( `source(none) disables looking at the module graph`, { @@ -453,8 +944,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -539,8 +1030,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -641,8 +1132,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -687,16 +1178,96 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { async ({ root, fs, exec, expect }) => { await expect(() => exec('pnpm vite build', { cwd: path.join(root, 'project-a') }, { ignoreStdErr: true }), - ).rejects.toThrowError('The `source(../i-do-not-exist)` does not exist') + ).rejects.toThrow('The `source(../i-do-not-exist)` does not exist') let files = await fs.glob('project-a/dist/**/*.css') expect(files).toHaveLength(0) }, ) + + test( + 'source(…) and `@source` are relative to the file they are in', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + `, + 'index.css': css` @import './project-a/src/index.css'; `, + + 'project-a/src/index.css': css` + /* Run auto-content detection in ../../project-b */ + @import 'tailwindcss/utilities' source('../../project-b'); + + /* Explicitly using node_modules in the @source allows git ignored folders */ + @source '../../project-c'; + `, + + // Project A is the current folder, but we explicitly configured + // `source(project-b)`, therefore project-a should not be included in + // the output. + 'project-a/src/index.html': html` +
+ `, + + // Project B is the configured `source(…)`, therefore auto source + // detection should include known extensions and folders in the output. + 'project-b/src/index.html': html` +
+ `, + + // Project C should apply auto source detection, therefore known + // extensions and folders should be included in the output. + 'project-c/src/index.html': html` +
+ `, + }, + }, + async ({ fs, exec, spawn, root, expect }) => { + await exec('pnpm vite build', { cwd: root }) + + let content = await fs.dumpFiles('./dist/assets/*.css') + + expect(content).not.toContain(candidate`content-['project-a/src/index.html']`) + expect(content).toContain(candidate`content-['project-b/src/index.html']`) + expect(content).toContain(candidate`content-['project-c/src/index.html']`) + }, + ) }) test( - `demote Tailwind roots to regular CSS files and back to Tailwind roots while restoring all candidates`, + `demote Tailwind roots to regular CSS files and back to Tailwind roots`, { fs: { 'package.json': json` @@ -707,7 +1278,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -750,19 +1321,9 @@ test( return Boolean(url) }) - // Candidates are resolved lazily, so the first visit of index.html - // will only have candidates from this file. await retryAssertion(async () => { let styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`underline`) - expect(styles).not.toContain(candidate`font-bold`) - }) - - // Going to about.html will extend the candidate list to include - // candidates from about.html. - await retryAssertion(async () => { - let styles = await fetchStyles(url, '/about.html') - expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`font-bold`) }) @@ -789,7 +1350,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -841,6 +1402,325 @@ test( }, ) +test( + `does not interfere with ?commonjs-proxy modules`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^", + "plotly.js": "^3", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + `, + 'src/index.js': js`import Plotly from 'plotly.js/lib/core'`, + + // `plotly.js` pulls in `es5-ext`, which has a build script. As of pnpm + // v10, build scripts only run when explicitly allowed. + 'pnpm-workspace.yaml': yaml` + # + allowBuilds: + es5-ext: true + `, + }, + }, + async ({ exec, expect, fs }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`maplibregl-map`]) + }, +) + function firstLine(str: string) { return str.split('\n')[0] } + +test( + 'optimize option: disabled', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss({ optimize: false })], + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ exec, expect, fs }) => { + await exec('pnpm vite build') + + // Should not be minified when optimize is disabled + expect((await fs.dumpFiles('dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-.css')) + .toMatchInlineSnapshot(` + " + --- dist/assets/index-.css --- + .focus\\:text-\\[black\\]:focus { + color: black; + } + " + `) + }, +) + +test( + 'optimize option: enabled with minify disabled', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss({ optimize: { minify: false } })], + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ exec, expect, fs }) => { + await exec('pnpm vite build') + + expect((await fs.dumpFiles('dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-.css')) + .toMatchInlineSnapshot(` + " + --- dist/assets/index-.css --- + @media (hover: hover) { + .hover\\:text-\\[black\\]:hover { + color: #000; + } + } + " + `) + }, +) + +test( + `the plugin works when using the environment API`, + { + fs: { + 'package.json': txt` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + plugins: [tailwindcss()], + builder: {}, + environments: { + server: { + build: { + cssMinify: false, + emitAssets: true, + rollupOptions: { input: './src/server.ts' }, + }, + }, + }, + }) + `, + // Has to exist or the build fails + 'index.html': html` +
+ `, + 'src/server.ts': js` + // Import the stylesheet in the server build + import a from './index.css?url' + console.log(a) + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ root, fs, exec, expect }) => { + await exec('pnpm vite build', { cwd: root }) + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`content-['index.html']`]) + }, +) + +// https://github.com/tailwindlabs/tailwindcss/issues/17532 +test( + 'deleting a file should not crash Vite', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "6.2.5" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + +
+ + + `, + 'src/index.css': css`@import 'tailwindcss';`, + 'src/main.js': js` + import iconUrl from './assets/icon.svg?url' + import './index.css' + + document.querySelector('#app').innerHTML = iconUrl + `, + 'src/assets/icon.svg': html` + + + + `, + }, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let [main, css] = await Promise.all([ + fetch(`${url}/src/main.js`).then((r) => r.text()), + fetch(`${url}/src/index.css`).then((r) => r.text()), + ]) + + expect(main).toContain('/src/assets/icon.svg?import&url') + expect(main).toContain('/src/index.css') + expect(css).toContain('.fill-red-500') + }) + + process.flush() + + let unexpectedError = process.onStderr((m) => { + return /error|crash|panic/i.test(m) + }) + + await fs.write( + 'src/main.js', + js` + import './index.css' + document.querySelector('#app').innerHTML = 'Hello World' + `, + ) + + await fs.delete('src/assets/icon.svg') + + let result = await Promise.race([ + unexpectedError.then(() => 'stderr'), + new Promise((resolve) => setTimeout(() => resolve('timeout'), 500)), + ]) + + expect(result).toBe('timeout') + + await retryAssertion(async () => { + let response = await fetch(`${url}/src/index.css?t=${Date.now()}`) + expect(response.status).toBe(200) + }) + }, +) diff --git a/integrations/vite/multi-root.test.ts b/integrations/vite/multi-root.test.ts index 39da43690..4020aff0f 100644 --- a/integrations/vite/multi-root.test.ts +++ b/integrations/vite/multi-root.test.ts @@ -12,7 +12,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -43,12 +43,12 @@ test( `, 'src/shared.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/root1.css': css` @import './shared.css'; - @variant one (&:is([data-root='1'])); + @custom-variant one (&:is([data-root='1'])); `, 'root2.html': html` @@ -60,7 +60,7 @@ test( `, 'src/root2.css': css` @import './shared.css'; - @variant two (&:is([data-root='2'])); + @custom-variant two (&:is([data-root='2'])); `, }, }, @@ -96,7 +96,7 @@ test( "tailwindcss": "workspace:^" }, "devDependencies": { - "vite": "^6" + "vite": "^7" } } `, @@ -119,12 +119,12 @@ test( `, 'src/shared.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/root1.css': css` @import './shared.css'; - @variant one (&:is([data-root='1'])); + @custom-variant one (&:is([data-root='1'])); `, 'root2.html': html` @@ -136,7 +136,7 @@ test( `, 'src/root2.css': css` @import './shared.css'; - @variant two (&:is([data-root='2'])); + @custom-variant two (&:is([data-root='2'])); `, }, }, diff --git a/integrations/vite/nuxt.test.ts b/integrations/vite/nuxt.test.ts index 1b834525c..20f367020 100644 --- a/integrations/vite/nuxt.test.ts +++ b/integrations/vite/nuxt.test.ts @@ -1,4 +1,15 @@ -import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' +import { + candidate, + css, + fetchStyles, + getRandomPort, + html, + json, + retryAssertion, + test, + ts, + yaml, +} from '../utils' const SETUP = { fs: { @@ -7,12 +18,18 @@ const SETUP = { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", - "nuxt": "^3.13.1", + "nuxt": "3.16.0", + "nitropack": "2.11.0", "tailwindcss": "workspace:^", "vue": "latest" } } `, + 'pnpm-workspace.yaml': yaml` + # + overrides: + nuxi: 3.28.0 + `, 'nuxt.config.ts': ts` import tailwindcss from '@tailwindcss/vite' @@ -76,7 +93,10 @@ test('dev mode', SETUP, async ({ fs, spawn, expect }) => { test('build', SETUP, async ({ spawn, exec, expect }) => { await exec('pnpm nuxt build') - let process = await spawn('pnpm nuxt preview', { + // The Nuxt preview server does not automatically assign a free port if 3000 + // is taken, so we use a random port instead. + let port = await getRandomPort() + let process = await spawn(`pnpm nuxt preview --port ${port}`, { env: { TEST: 'false', NODE_ENV: 'development', diff --git a/integrations/vite/other-transforms.test.ts b/integrations/vite/other-transforms.test.ts index 6aef096a7..d56396065 100644 --- a/integrations/vite/other-transforms.test.ts +++ b/integrations/vite/other-transforms.test.ts @@ -13,8 +13,8 @@ function createSetup(transformer: 'postcss' | 'lightningcss') { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} - "vite": "^6" + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} + "vite": "^7" } } `, @@ -47,7 +47,7 @@ function createSetup(transformer: 'postcss' | 'lightningcss') { `, 'src/index.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; .foo { @@ -111,7 +111,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => { await fs.write( 'src/index.css', css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; .foo { @@ -155,7 +155,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => { await fs.write( 'src/index.css', css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; .foo { diff --git a/integrations/vite/preact.test.ts b/integrations/vite/preact.test.ts new file mode 100644 index 000000000..8e4deec2b --- /dev/null +++ b/integrations/vite/preact.test.ts @@ -0,0 +1,165 @@ +import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts, txt } from '../utils' + +test( + 'dev mode', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "preact": "^10" + }, + "devDependencies": { + "@preact/preset-vite": "^2", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^", + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import fs from 'node:fs' + import path from 'node:path' + import preact from '@preact/preset-vite' + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + plugins: [ + tailwindcss(), + preact(), + { + // Log all HMR payloads to a file so the test can assert on them + name: 'hmr-wiretap', + configureServer(server) { + let logFile = path.resolve('hmr.log') + fs.writeFileSync(logFile, '') + for (let environment of Object.values(server.environments)) { + let send = environment.hot.send.bind(environment.hot) + environment.hot.send = (payload) => { + fs.appendFileSync(logFile, JSON.stringify(payload) + '\\n') + return send(payload) + } + } + }, + }, + ], + }) + `, + 'index.html': html` + + + + + +
+ + + + `, + 'src/main.tsx': ts` + import { render } from 'preact' + import { App } from './app' + + render(, document.getElementById('app')!) + `, + 'src/app.tsx': ts` + import { useState } from 'preact/hooks' + + export function App() { + const [count, setCount] = useState(0) + return ( + + ) + } + `, + 'src/index.css': css`@import 'tailwindcss';`, + }, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`underline`) + }) + + // Load the component modules, like a browser visiting the page would + await fetch(`${url}/src/main.tsx`) + await fetch(`${url}/src/app.tsx`) + + // Editing a component keeps HMR intact: new classes are delivered through + // a regular update, not a full page reload (which would lose all state) + { + await fs.write( + 'src/app.tsx', + ts` + import { useState } from 'preact/hooks' + + export function App() { + const [count, setCount] = useState(0) + return ( + + ) + } + `, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`flex`) + }) + expect(await fs.read('hmr.log')).toContain('"type":"update"') + expect(await fs.read('hmr.log')).not.toContain('full-reload') + } + + // Changing a scanned file that is not part of the module graph (e.g. + // `package.json`, which package managers and other tooling write to while + // the dev server is running) should not trigger a full reload either — + // that would destroy client state. New candidates should still be picked + // up because the file is a watch dependency of the CSS root, so the CSS + // hot-updates through Vite's regular pipeline. + // + // https://github.com/tailwindlabs/tailwindcss/issues/20411 + { + await fs.write( + 'package.json', + txt` + { + "type": "module", + "description": "content-['package.json']", + "dependencies": { + "preact": "^10" + }, + "devDependencies": { + "@preact/preset-vite": "^2", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^", + "vite": "^8" + } + } + `, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url) + expect(styles).toContain(candidate`content-['package.json']`) + }) + expect(await fs.read('hmr.log')).not.toContain('full-reload') + } + }, +) diff --git a/integrations/vite/qwik.test.ts b/integrations/vite/qwik.test.ts new file mode 100644 index 000000000..42db6563a --- /dev/null +++ b/integrations/vite/qwik.test.ts @@ -0,0 +1,100 @@ +import { candidate, css, fetchStyles, json, retryAssertion, test, ts } from '../utils' + +test( + 'dev mode', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@builder.io/qwik": "^1", + "@builder.io/qwik-city": "^1", + "vite": "^5", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + } + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import { qwikVite } from '@builder.io/qwik/optimizer' + import { qwikCity } from '@builder.io/qwik-city/vite' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig(() => { + return { + plugins: [tailwindcss(), qwikCity(), qwikVite()], + } + }) + `, + 'src/root.tsx': ts` + import { component$ } from '@builder.io/qwik' + import { QwikCityProvider, RouterOutlet } from '@builder.io/qwik-city' + + import './global.css' + + export default component$(() => { + return ( + + + + + + + ) + }) + `, + 'src/global.css': css`@import 'tailwindcss/utilities.css';`, + 'src/entry.ssr.tsx': ts` + import { renderToStream, type RenderToStreamOptions } from '@builder.io/qwik/server' + import Root from './root' + + export default function (opts: RenderToStreamOptions) { + return renderToStream(, opts) + } + `, + 'src/routes/index.tsx': ts` + import { component$ } from '@builder.io/qwik' + + export default component$(() => { + return

Hello World!

+ }) + `, + }, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm vite --mode ssr') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + console.log(m) + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let css = await fetchStyles(url) + expect(css).toContain(candidate`underline`) + }) + + await retryAssertion(async () => { + await fs.write( + 'src/routes/index.tsx', + ts` + import { component$ } from '@builder.io/qwik' + + export default component$(() => { + return

Hello World!

+ }) + `, + ) + + let css = await fetchStyles(url) + expect(css).toContain(candidate`underline`) + expect(css).toContain(candidate`flex`) + }) + }, +) diff --git a/integrations/vite/react-router.test.ts b/integrations/vite/react-router.test.ts new file mode 100644 index 000000000..98b0c928d --- /dev/null +++ b/integrations/vite/react-router.test.ts @@ -0,0 +1,266 @@ +import { candidate, css, fetchStyles, json, retryAssertion, test, ts, txt } from '../utils' + +const WORKSPACE = { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@react-router/dev": "^7", + "@react-router/node": "^7", + "@react-router/serve": "^7", + "@tailwindcss/vite": "workspace:^", + "@types/node": "^20", + "@types/react-dom": "^19", + "@types/react": "^19", + "isbot": "^5", + "react-dom": "^19", + "react-router": "^7", + "react": "^19", + "tailwindcss": "workspace:^", + "vite": "^5" + } + } + `, + 'react-router.config.ts': ts` + import type { Config } from '@react-router/dev/config' + export default { ssr: true } satisfies Config + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import { reactRouter } from '@react-router/dev/vite' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [tailwindcss(), reactRouter()], + }) + `, + 'app/routes/home.tsx': ts` + export default function Home() { + return

Welcome to React Router

+ } + `, + 'app/app.css': css`@import 'tailwindcss';`, + 'app/routes.ts': ts` + import { type RouteConfig, index } from '@react-router/dev/routes' + export default [index('routes/home.tsx')] satisfies RouteConfig + `, + 'app/root.tsx': ts` + import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' + import './app.css' + export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ) + } + + export default function App() { + return + } + `, +} + +test('dev mode', { fs: WORKSPACE }, async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm react-router dev') + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let css = await fetchStyles(url) + expect(css).toContain(candidate`font-bold`) + }) + + await retryAssertion(async () => { + await fs.write( + 'app/routes/home.tsx', + ts` + export default function Home() { + return

Welcome to React Router

+ } + `, + ) + + let css = await fetchStyles(url) + expect(css).toContain(candidate`underline`) + expect(css).toContain(candidate`font-bold`) + }) +}) + +test( + // cf. https://github.com/remix-run/react-router/blob/00cb4d7b310663b2e84152700c05d3b503005e83/integration/vite-hmr-hdr-test.ts#L311-L318 + 'dev mode, editing a server-only loader dependency triggers HDR instead of a full reload', + { + fs: { + ...WORKSPACE, + 'package.json': json` + { + "type": "module", + "dependencies": { + "@react-router/dev": "^7", + "@react-router/node": "^7", + "@react-router/serve": "^7", + "@tailwindcss/vite": "workspace:^", + "@types/node": "^20", + "@types/react-dom": "^19", + "@types/react": "^19", + "isbot": "^5", + "react-dom": "^19", + "react-router": "^7", + "react": "^19", + "tailwindcss": "workspace:^", + "vite": "^7" + } + } + `, + 'app/routes/home.tsx': ts` + import type { Route } from './+types/home' + import { direct } from '../direct-hdr-dep' + + export async function loader() { + return { message: direct } + } + + export default function Home({ loaderData }: Route.ComponentProps) { + return ( +
+

{loaderData.message}

+ +
+ ) + } + `, + 'app/direct-hdr-dep.ts': ts` export const direct = 'HDR: 0' `, + }, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm react-router dev') + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + // check initial state + await retryAssertion(async () => { + let html = await (await fetch(url)).text() + expect(html).toContain('HDR: 0') + + let css = await fetchStyles(url) + expect(css).toContain(candidate`font-bold`) + }) + + // Flush stdout so we only see messages triggered by the edit below. + process.flush() + + // Edit the server-only module. The client environment watches this file + // but it only exists in the server module graph. Without the fix, the + // Tailwind CSS plugin would trigger a full page reload on the client + // instead of letting react-router handle HDR. + await fs.write('app/direct-hdr-dep.ts', ts` export const direct = 'HDR: 1' `) + + // check update + await retryAssertion(async () => { + let html = await (await fetch(url)).text() + expect(html).toContain('HDR: 1') + + let css = await fetchStyles(url) + expect(css).toContain(candidate`font-bold`) + }) + + // Assert the client receives an HMR update (not a full page reload). + await process.onStdout((m) => m.includes('(client) hmr update')) + }, +) + +test('build mode', { fs: WORKSPACE }, async ({ spawn, exec, expect }) => { + await exec('pnpm react-router build') + let process = await spawn('pnpm react-router-serve ./build/server/index.js') + + let url = '' + await process.onStdout((m) => { + let match = /\[react-router-serve\]\s*(http.*) \/?/.exec(m) + if (match) url = match[1] + return url != '' + }) + + await retryAssertion(async () => { + let css = await fetchStyles(url) + expect(css).toContain(candidate`font-bold`) + }) +}) + +test( + 'build mode using ?url stylesheet imports should only build one stylesheet (requires `file-system` scanner)', + { + fs: { + ...WORKSPACE, + 'app/root.tsx': ts` + import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' + import styles from './app.css?url' + export const links: Route.LinksFunction = () => [{ rel: 'stylesheet', href: styles }] + export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ) + } + + export default function App() { + return + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import { reactRouter } from '@react-router/dev/vite' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [tailwindcss(), reactRouter()], + }) + `, + '.gitignore': txt` + node_modules/ + build/ + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm react-router build') + + let files = await fs.glob('build/client/assets/**/*.css') + + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`font-bold`]) + }, +) diff --git a/integrations/vite/resolvers.test.ts b/integrations/vite/resolvers.test.ts index 040f84391..fc9aca5c7 100644 --- a/integrations/vite/resolvers.test.ts +++ b/integrations/vite/resolvers.test.ts @@ -1,5 +1,661 @@ import { describe } from 'vitest' -import { candidate, css, fetchStyles, html, js, retryAssertion, test, ts, txt } from '../utils' +import { + candidate, + css, + fetchStyles, + html, + js, + json, + retryAssertion, + test, + ts, + txt, + yaml, +} from '../utils' + +test( + 'resolves tsconfig paths in production build', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'tsconfig.json': json` + { + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + resolve: { + tsconfigPaths: true, + }, + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @import '@/styles/base.css'; + @plugin '@/plugin.js'; + `, + 'src/styles/base.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + 'src/plugin.js': js` + export default function ({ addUtilities }) { + addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) + } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`underline`, candidate`custom-underline`]) + }, +) + +test( + 'resolves tsconfig paths in dev mode', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'tsconfig.json': json` + { + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + resolve: { + tsconfigPaths: true, + }, + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @import '@/styles/base.css'; + @plugin '@/plugin.js'; + `, + 'src/styles/base.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + 'src/plugin.js': js` + export default function ({ addUtilities }) { + addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) + } + `, + }, + }, + async ({ spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`underline`) + expect(styles).toContain(candidate`custom-underline`) + }) + }, +) + +test( + 'resolves at-sign aliases in production build', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + import { fileURLToPath } from 'node:url' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + resolve: { + alias: [{ find: '@', replacement: fileURLToPath(new URL('.', import.meta.url)) }], + }, + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @import '@/src/styles/base.css'; + @plugin '@/src/plugin.js'; + `, + 'src/styles/base.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + 'src/plugin.js': js` + export default function ({ addUtilities }) { + addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) + } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`underline`, candidate`custom-underline`]) + }, +) + +test( + 'resolves package plugins in production build with at-sign aliases', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "tailwind-scrollbar": "^4.0.2", + "tailwindcss-motion": "^1.1.1", + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + import { fileURLToPath } from 'node:url' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + resolve: { + alias: [{ find: '@', replacement: fileURLToPath(new URL('.', import.meta.url)) }], + }, + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin 'tailwind-scrollbar'; + @plugin 'tailwindcss-motion'; + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [ + candidate`scrollbar`, + candidate`scrollbar-thumb-red-500`, + candidate`motion-preset-fade`, + ]) + }, +) + +test( + 'resolves package plugins to JS entries in production build when browser points to CSS', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "plugin-browser-css": "workspace:*", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'pnpm-workspace.yaml': yaml` + # + packages: + - packages/* + `, + 'packages/plugin-browser-css/package.json': json` + { + "name": "plugin-browser-css", + "version": "1.0.0", + "type": "module", + "main": "./index.js", + "module": "./index.js", + "browser": "./browser.css" + } + `, + 'packages/plugin-browser-css/index.js': js` + export default function ({ addUtilities }) { + addUtilities({ '.browser-css-plugin': { 'border-bottom': '1px solid green' } }) + } + `, + 'packages/plugin-browser-css/browser.css': css` + .should-not-be-loaded-as-a-plugin { + display: none; + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + +
Hello, world!
+ + + `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin 'plugin-browser-css'; + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + await fs.expectFileToContain(filename, [candidate`browser-css-plugin`]) + }, +) + +test( + 'resolves package plugins to JS entries in dev mode when browser points to CSS', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "plugin-browser-css": "workspace:*", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'pnpm-workspace.yaml': yaml` + # + packages: + - packages/* + `, + 'packages/plugin-browser-css/package.json': json` + { + "name": "plugin-browser-css", + "version": "1.0.0", + "type": "module", + "main": "./index.js", + "module": "./index.js", + "browser": "./browser.css" + } + `, + 'packages/plugin-browser-css/index.js': js` + export default function ({ addUtilities }) { + addUtilities({ '.browser-css-plugin': { 'border-bottom': '1px solid green' } }) + } + `, + 'packages/plugin-browser-css/browser.css': css` + .should-not-be-loaded-as-a-plugin { + display: none; + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + +
Hello, world!
+ + + `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin 'plugin-browser-css'; + `, + }, + }, + async ({ spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`browser-css-plugin`) + }) + }, +) + +test( + 'resolves package plugins in dev mode when package exports CSS files', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "daisyui": "^5", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + + + + + `, + 'src/index.css': css` + @import 'tailwindcss'; + @plugin 'daisyui'; + `, + }, + }, + async ({ spawn, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain('.btn') + expect(styles).toContain('.btn-primary') + }) + }, +) + +test( + 'resolve relative CSS files correctly', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + + + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + @import './themes/glow.css'; + `, + // References a file in the current folder, which names happens to match a + // file in the parent folder as well. + 'src/themes/glow.css': css`@import './entry.css';`, + 'src/themes/entry.css': css` + .do-include-me { + color: green; + } + `, + + // Never rerefenced, so should not be included + 'src/entry.css': css` + .do-not-include-me { + color: red; + } + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm vite build') + + expect( + (await fs.dumpFiles('./dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-.css'), + ).toMatchInlineSnapshot(` + " + --- ./dist/assets/index-.css --- + .do-include-me { + color: green; + } + " + `) + }, +) + +test( + 'resolve relative JS files correctly', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + + + + + + + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + @import './themes/glow.css'; + `, + // References a file in the current folder, which names happens to match a + // file in the parent folder as well. + 'src/themes/glow.css': css`@plugin "./my-plugin.js";`, + 'src/themes/my-plugin.js': ts` + export default function ({ addBase }) { + addBase({ '.do-include-me': { color: 'green' } }) + } + `, + + // Never rerefenced, so should not be included + 'src/my-plugin.js': css` + export default function ({ addBase }) { + addBase({ '.do-not-include-me': { 'color': 'red' } }) + } + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm vite build') + + expect( + (await fs.dumpFiles('./dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-.css'), + ).toMatchInlineSnapshot(` + " + --- ./dist/assets/index-.css --- + @layer base { + .do-include-me { + color: green; + } + } + " + `) + }, +) describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { test( @@ -14,7 +670,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} "vite": "^5.3.5" } } @@ -49,7 +705,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { @plugin '#js-alias'; `, 'src/alias.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` @@ -82,7 +738,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} "vite": "^5.3.5" } } @@ -117,7 +773,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { @plugin '#js-alias'; `, 'src/alias.css': css` - @import 'tailwindcss/theme' theme(reference); + @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` diff --git a/integrations/vite/solidstart.test.ts b/integrations/vite/solidstart.test.ts new file mode 100644 index 000000000..bbe9ccc88 --- /dev/null +++ b/integrations/vite/solidstart.test.ts @@ -0,0 +1,108 @@ +import { candidate, css, fetchStyles, js, json, retryAssertion, test, ts } from '../utils' + +const WORKSPACE = { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@solidjs/start": "^1", + "solid-js": "^1", + "vinxi": "^0", + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + } + } + `, + 'jsconfig.json': json` + { + "compilerOptions": { + "jsx": "preserve", + "jsxImportSource": "solid-js" + } + } + `, + 'app.config.js': ts` + import { defineConfig } from '@solidjs/start/config' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + vite: { + plugins: [tailwindcss()], + }, + }) + `, + 'src/entry-server.jsx': js` + // @refresh reload + import { createHandler, StartServer } from '@solidjs/start/server' + + export default createHandler(() => ( + ( + + {assets} + +
{children}
+ {scripts} + + + )} + /> + )) + `, + 'src/entry-client.jsx': js` + // @refresh reload + import { mount, StartClient } from '@solidjs/start/client' + + mount(() => , document.getElementById('app')) + `, + 'src/app.jsx': js` + import './app.css' + export default function App() { + return

Hello world!

+ } + `, + 'src/app.css': css`@import 'tailwindcss';`, +} + +test( + 'dev mode', + { + fs: WORKSPACE, + }, + async ({ fs, spawn, expect }) => { + let process = await spawn('pnpm vinxi dev', { + env: { + TEST: 'false', // VERY IMPORTANT OTHERWISE YOU WON'T GET OUTPUT + NODE_ENV: 'development', + }, + }) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let css = await fetchStyles(url) + expect(css).toContain(candidate`underline`) + }) + + await retryAssertion(async () => { + await fs.write( + 'src/app.jsx', + js` + import './app.css' + export default function App() { + return

Hello world!

+ } + `, + ) + + let css = await fetchStyles(url) + expect(css).toContain(candidate`underline`) + expect(css).toContain(candidate`font-bold`) + }) + }, +) diff --git a/integrations/vite/source-maps.test.ts b/integrations/vite/source-maps.test.ts new file mode 100644 index 000000000..fe26d9a7c --- /dev/null +++ b/integrations/vite/source-maps.test.ts @@ -0,0 +1,157 @@ +import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' + +test( + `dev build`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "lightningcss": "^1", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + plugins: [tailwindcss()], + css: { + devSourcemap: true, + }, + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @import 'tailwindcss/utilities'; + /* */ + `, + }, + }, + async ({ fs, spawn, expect, parseSourceMap }) => { + // Source maps only work in development mode in Vite + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + let styles = await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + + // Wait until we have the right CSS + expect(styles).toContain(candidate`flex`) + + return styles + }) + + // Make sure we can find a source map + let map = parseSourceMap(styles) + + expect(map.at(1, 0)).toMatchObject({ + source: null, + original: '(none)', + generated: '/*! tailwi...', + }) + + expect(map.at(2, 0)).toMatchObject({ + source: expect.stringContaining('utilities.css'), + original: '@tailwind...', + generated: '.flex {...', + }) + + expect(map.at(3, 2)).toMatchObject({ + source: expect.stringContaining('utilities.css'), + original: '@tailwind...', + generated: 'display: f...', + }) + + expect(map.at(4, 0)).toMatchObject({ + source: null, + original: '(none)', + generated: '}...', + }) + }, +) + +// https://github.com/tailwindlabs/tailwindcss/issues/19930 +test( + 'production build without Tailwind roots should not result in source map warnings', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [ + tailwindcss(), + { + name: 'inspect-source-map-chain', + enforce: 'pre', + transform(_, id) { + if (id.includes('.css')) { + // Force Rollup to collapse the sourcemap chain during this build. + this.getCombinedSourcemap() + } + }, + }, + ], + css: { + devSourcemap: true, + }, + build: { + sourcemap: true, + }, + }) + `, + 'index.html': html` + + +
Hello, world!
+ + `, + 'src/index.js': ts` import './index.css' `, + 'src/index.css': css` + body { + color: red; + } + `, + }, + }, + async ({ exec, expect }) => { + let output = await exec('pnpm vite build') + + expect(output).not.toContain('Sourcemap is likely to be incorrect') + }, +) diff --git a/integrations/vite/ssr.test.ts b/integrations/vite/ssr.test.ts index 3c3f1cc4b..a1d660816 100644 --- a/integrations/vite/ssr.test.ts +++ b/integrations/vite/ssr.test.ts @@ -1,138 +1,72 @@ +import { describe } from 'vitest' import { candidate, css, html, json, test, ts } from '../utils' -test( - 'Vite 5', - { - fs: { - 'package.json': json` - { - "type": "module", - "dependencies": { - "@tailwindcss/vite": "workspace:^", - "tailwindcss": "workspace:^" - }, - "_comment": "This test uses Vite 5.3 on purpose. Do not upgrade it to Vite 6.", - "devDependencies": { - "vite": "^5.3" +describe.each([['^5.3'], ['^6.0'], ['^7'], ['^8']])('Using Vite %s', (version) => { + test( + `SSR build`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "${version}" + } } - } - `, - 'vite.config.ts': ts` - import tailwindcss from '@tailwindcss/vite' - import { defineConfig } from 'vite' + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' - export default defineConfig({ - build: { - cssMinify: false, - ssrEmitAssets: true, - }, - plugins: [tailwindcss()], - ssr: { resolve: { conditions: [] } }, - }) - `, - 'index.html': html` - -
- - - `, - 'src/index.css': css`@import 'tailwindcss';`, - 'src/index.ts': ts` - import './index.css' + export default defineConfig({ + build: { + cssMinify: false, + ssrEmitAssets: true, + }, + plugins: [tailwindcss()], + }) + `, + 'index.html': html` + +
+ + + `, + 'src/index.css': css`@import 'tailwindcss';`, + 'src/index.ts': ts` + import './index.css' - document.querySelector('#app').innerHTML = \` -
Hello, world!
- \` - `, - 'server.ts': ts` - import css from './src/index.css?url' + document.querySelector('#app').innerHTML = \` +
Hello, world!
+ \` + `, + 'server.ts': ts` + import css from './src/index.css?url' - document.querySelector('#app').innerHTML = \` - -
Hello, world!
- \` - `, + document.querySelector('#app').innerHTML = \` + +
Hello, world!
+ \` + `, + }, }, - }, - async ({ fs, exec, expect }) => { - await exec('pnpm vite build --ssr server.ts') + async ({ fs, exec, expect }) => { + await exec('pnpm vite build --ssr server.ts') - let files = await fs.glob('dist/**/*.css') - expect(files).toHaveLength(1) - let [filename] = files[0] + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] - await fs.expectFileToContain(filename, [ - // - candidate`underline`, - candidate`m-2`, - ]) - }, -) - -test( - `Vite 6`, - { - fs: { - 'package.json': json` - { - "type": "module", - "dependencies": { - "@tailwindcss/vite": "workspace:^", - "tailwindcss": "workspace:^" - }, - "devDependencies": { - "vite": "^6.0" - } - } - `, - 'vite.config.ts': ts` - import tailwindcss from '@tailwindcss/vite' - import { defineConfig } from 'vite' - - export default defineConfig({ - build: { - cssMinify: false, - ssrEmitAssets: true, - }, - plugins: [tailwindcss()], - ssr: { resolve: { conditions: [] } }, - }) - `, - 'index.html': html` - -
- - - `, - 'src/index.css': css`@import 'tailwindcss';`, - 'src/index.ts': ts` - import './index.css' - - document.querySelector('#app').innerHTML = \` -
Hello, world!
- \` - `, - 'server.ts': ts` - import css from './src/index.css?url' - - document.querySelector('#app').innerHTML = \` - -
Hello, world!
- \` - `, + await fs.expectFileToContain(filename, [ + candidate`underline`, + candidate`m-2`, + candidate`overline`, + candidate`m-3`, + ]) }, - }, - async ({ fs, exec, expect }) => { - await exec('pnpm vite build --ssr server.ts') - - let files = await fs.glob('dist/**/*.css') - expect(files).toHaveLength(1) - let [filename] = files[0] - - await fs.expectFileToContain(filename, [ - // - candidate`underline`, - candidate`m-2`, - ]) - }, -) + ) +}) diff --git a/integrations/vite/svelte.test.ts b/integrations/vite/svelte.test.ts index 33eca5a13..e186c3f85 100644 --- a/integrations/vite/svelte.test.ts +++ b/integrations/vite/svelte.test.ts @@ -14,7 +14,7 @@ test( "devDependencies": { "@sveltejs/vite-plugin-svelte": "^5", "@tailwindcss/vite": "workspace:^", - "vite": "^6" + "vite": "^7" } } `, @@ -57,19 +57,19 @@ test(

Hello {name}!

`, 'src/other.css': css` .local { @apply text-red-500; - animation: 2s ease-in-out 0s infinite localKeyframes; + animation: 2s ease-in-out infinite localKeyframes; } :global(.global) { @apply text-green-500; - animation: 2s ease-in-out 0s infinite globalKeyframes; + animation: 2s ease-in-out infinite globalKeyframes; } @keyframes -global-globalKeyframes { @@ -93,18 +93,21 @@ test( }, }, async ({ exec, fs, expect }) => { - await exec('pnpm vite build') + let output = await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) await fs.expectFileToContain(files[0][0], [ candidate`underline`, - '.global{color:var(--color-green-500);animation:2s ease-in-out 0s infinite globalKeyframes}', - /\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out 0s infinite svelte-.*-localKeyframes\}/, + '.global{color:var(--color-green-500,oklch(72.3% .219 149.579));animation:2s ease-in-out infinite globalKeyframes}', + /\.local.svelte-.*\{color:var\(--color-red-500,oklch\(63\.7% \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/, /@keyframes globalKeyframes\{/, /@keyframes svelte-.*-localKeyframes\{/, ]) + + // Should not print any warnings + expect(output).not.toContain('vite-plugin-svelte') }, ) @@ -122,7 +125,7 @@ test( "devDependencies": { "@sveltejs/vite-plugin-svelte": "^5", "@tailwindcss/vite": "workspace:^", - "vite": "^6" + "vite": "^7" } } `, @@ -164,20 +167,20 @@ test(

Hello {name}!

`, 'src/index.css': css` @import 'tailwindcss'; `, 'src/other.css': css` .local { @apply text-red-500; - animation: 2s ease-in-out 0s infinite localKeyframes; + animation: 2s ease-in-out infinite localKeyframes; } :global(.global) { @apply text-green-500; - animation: 2s ease-in-out 0s infinite globalKeyframes; + animation: 2s ease-in-out infinite globalKeyframes; } @keyframes -global-globalKeyframes { @@ -210,10 +213,10 @@ test( let [, css] = files[0] expect(css).toContain(candidate`underline`) expect(css).toContain( - '.global{color:var(--color-green-500);animation:2s ease-in-out 0s infinite globalKeyframes}', + '.global{color:var(--color-green-500,oklch(72.3% .219 149.579));animation:2s ease-in-out infinite globalKeyframes}', ) expect(css).toMatch( - /\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out 0s infinite svelte-.*-localKeyframes\}/, + /\.local.svelte-.*\{color:var\(--color-red-500,oklch\(63\.7% \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/, ) expect(css).toMatch(/@keyframes globalKeyframes\{/) expect(css).toMatch(/@keyframes svelte-.*-localKeyframes\{/) @@ -235,14 +238,16 @@ test( let [, css] = files[0] expect(css).toContain(candidate`font-bold`) expect(css).toContain( - '.global{color:var(--color-green-500);animation:2s ease-in-out 0s infinite globalKeyframes}', + '.global{color:var(--color-green-500,oklch(72.3% .219 149.579));animation:2s ease-in-out infinite globalKeyframes}', ) expect(css).toMatch( - /\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out 0s infinite svelte-.*-localKeyframes\}/, + /\.local.svelte-.*\{color:var\(--color-red-500,oklch\(63\.7% \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/, ) expect(css).toMatch(/@keyframes globalKeyframes\{/) expect(css).toMatch(/@keyframes svelte-.*-localKeyframes\{/) - expect(css).toMatch(/\.bar.svelte-.*\{color:var\(--color-pink-500\)\}/) + expect(css).toMatch( + /\.bar.svelte-.*\{color:var\(--color-pink-500,oklch\(65\.6% \.241 354\.308\)\)\}/, + ) }) }, ) diff --git a/integrations/vite/sveltekit.test.ts b/integrations/vite/sveltekit.test.ts new file mode 100644 index 000000000..92e855da9 --- /dev/null +++ b/integrations/vite/sveltekit.test.ts @@ -0,0 +1,80 @@ +import { candidate, css, html, js, json, test, ts } from '../utils' + +test( + 'sveltekit build resolves package plugins', + { + fs: { + 'package.json': json` + { + "type": "module", + "scripts": { + "prepare": "svelte-kit sync || echo ''" + }, + "dependencies": { + "svelte": "^5", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^6", + "@sveltejs/kit": "^2", + "@tailwindcss/forms": "^0.5.11", + "@tailwindcss/vite": "workspace:^", + "vite": "^8" + } + } + `, + 'svelte.config.js': js` + import adapter from '@sveltejs/adapter-auto' + + export default { + kit: { + adapter: adapter(), + }, + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { sveltekit } from '@sveltejs/kit/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss(), sveltekit()], + }) + `, + 'src/app.html': html` + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + + `, + 'src/routes/+layout.svelte': html` + + + + `, + 'src/routes/+page.svelte': html` +
Hello, world!
+ `, + 'src/app.css': css` + @import 'tailwindcss'; + @plugin '@tailwindcss/forms'; + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('.svelte-kit/output/client/**/*.css') + expect(files.length).toBeGreaterThan(0) + + await fs.expectFileToContain(files[0][0], [candidate`form-input`]) + }, +) diff --git a/integrations/vite/url-rewriting.test.ts b/integrations/vite/url-rewriting.test.ts index a7c6433d4..500fe6d71 100644 --- a/integrations/vite/url-rewriting.test.ts +++ b/integrations/vite/url-rewriting.test.ts @@ -15,9 +15,9 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { "tailwindcss": "workspace:^" }, "devDependencies": { - ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''} + ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} "@tailwindcss/vite": "workspace:^", - "vite": "^6" + "vite": "^7" } } `, @@ -42,31 +42,36 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
- `, - 'src/main.ts': ts``, 'src/app.css': css` + @reference 'tailwindcss'; @import './dir-1/bar.css'; @import './dir-1/dir-2/baz.css'; @import './dir-1/dir-2/vector.css'; `, 'src/dir-1/bar.css': css` - .bar { + .test1 { background-image: url('../../resources/image.png'); } `, 'src/dir-1/dir-2/baz.css': css` - .baz { + .test2 { background-image: url('../../../resources/image.png'); } `, 'src/dir-1/dir-2/vector.css': css` - .baz { + @import './dir-3/vector.css'; + .test3 { background-image: url('../../../resources/vector.svg'); } `, + 'src/dir-1/dir-2/dir-3/vector.css': css` + .test4 { + background-image: url('./vector-2.svg'); + } + `, 'resources/image.png': binary(SIMPLE_IMAGE), 'resources/vector.svg': svg` @@ -76,6 +81,14 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { `, + 'src/dir-1/dir-2/dir-3/vector-2.svg': svg` + + + + + + + `, }, }, async ({ fs, exec, expect }) => { @@ -87,7 +100,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { await fs.expectFileToContain(files[0][0], [SIMPLE_IMAGE]) let images = await fs.glob('dist/**/*.svg') - expect(images).toHaveLength(1) + expect(images).toHaveLength(2) await fs.expectFileToContain(files[0][0], [/\/assets\/vector-.*?\.svg/]) }, diff --git a/integrations/vite/virtual-modules.test.ts b/integrations/vite/virtual-modules.test.ts new file mode 100644 index 000000000..12eadb6c7 Binary files /dev/null and b/integrations/vite/virtual-modules.test.ts differ diff --git a/integrations/vite/vue.test.ts b/integrations/vite/vue.test.ts index 7b5da2904..b04f57c6b 100644 --- a/integrations/vite/vue.test.ts +++ b/integrations/vite/vue.test.ts @@ -1,4 +1,5 @@ -import { candidate, html, json, test, ts } from '../utils' +import { stripVTControlCharacters } from 'node:util' +import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' test( 'production build', @@ -14,7 +15,7 @@ test( "devDependencies": { "@vitejs/plugin-vue": "^5.1.2", "@tailwindcss/vite": "workspace:^", - "vite": "^6" + "vite": "^7" } } `, @@ -71,3 +72,309 @@ test( await fs.expectFileToContain(files[0][0], ['.bar{']) }, ) + +{ + const VUE_COMPONENT_COUNT = 1_000 + + let vueComponentsWithReferences = Object.fromEntries( + Array.from({ length: VUE_COMPONENT_COUNT }, (_, idx) => [ + `src/components/Component${idx}.vue`, + html` + + + + `, + ]), + ) + + let vueComponentImports = Array.from( + { length: VUE_COMPONENT_COUNT }, + (_, idx) => `import Component${idx} from './components/Component${idx}.vue'`, + ).join('\n') + + let vueComponentUsages = Array.from( + { length: VUE_COMPONENT_COUNT }, + (_, idx) => ``, + ).join('\n') + + test( + 'production build with many Vue style blocks referencing the main stylesheet', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "vue": "^3.4.37", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.2", + "@tailwindcss/vite": "workspace:^", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import vue from '@vitejs/plugin-vue' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [vue(), tailwindcss()], + }) + `, + 'index.html': html` + + + +
+ + + + `, + 'src/main.css': css`@import 'tailwindcss';`, + 'src/main.ts': ts` + import { createApp } from 'vue' + import './main.css' + import App from './App.vue' + + createApp(App).mount('#app') + `, + 'src/App.vue': html` + + + + `, + ...vueComponentsWithReferences, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + + await fs.expectFileToContain(files[0][0], [ + candidate`content-['component-0']`, + candidate`component-0`, + candidate`content-['component-99']`, + candidate`component-99`, + candidate`content-['component-999']`, + candidate`component-999`, + ]) + }, + ) +} + +test( + 'error when using `@apply` without `@reference`', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "vue": "^3.4.37", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.2", + "@tailwindcss/vite": "workspace:^", + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import { defineConfig } from 'vite' + import vue from '@vitejs/plugin-vue' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [vue(), tailwindcss()], + }) + `, + 'index.html': html` + + + +
+ + + + `, + 'src/main.ts': ts` + import { createApp } from 'vue' + import App from './App.vue' + + createApp(App).mount('#app') + `, + 'src/App.vue': html` + + + + `, + }, + }, + async ({ exec, expect }) => { + expect.assertions(1) + + try { + await exec('pnpm vite build', {}, { ignoreStdErr: true }) + } catch (error) { + let [, message] = + /error during build:([\s\S]*?)file:/g.exec( + stripVTControlCharacters(error.message.replace(/\r?\n/g, '\n')), + ) ?? [] + expect(message.trim()).toMatchInlineSnapshot( + `"[@tailwindcss/vite:generate:build] Cannot apply unknown utility class \`text-red-500\`. Are you using CSS modules or similar and missing \`@reference\`? https://tailwindcss.com/docs/functions-and-directives#reference-directive"`, + ) + } + }, +) + +// https://github.com/tailwindlabs/tailwindcss/issues/20320 +test( + 'editing a scanned `.vue` file that is not loaded as a module does not trigger a full reload', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "vue": "^3.4.37", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^6", + "@tailwindcss/vite": "workspace:^", + "vite": "^8" + } + } + `, + 'vite.config.ts': ts` + import fs from 'node:fs' + import path from 'node:path' + import { defineConfig } from 'vite' + import vue from '@vitejs/plugin-vue' + import tailwindcss from '@tailwindcss/vite' + + export default defineConfig({ + plugins: [ + vue(), + tailwindcss(), + { + // Log update and full-reload HMR payloads to a file so the + // test can assert on them. Custom events are not logged + // because \`@vitejs/plugin-vue\` sends a \`file-changed\` event + // for every file change, including changes to the log file + // itself, which would cause an infinite feedback loop. + name: 'hmr-wiretap', + configureServer(server) { + let logFile = path.resolve('hmr.log') + fs.writeFileSync(logFile, '') + for (let environment of Object.values(server.environments)) { + let send = environment.hot.send.bind(environment.hot) + environment.hot.send = (payload) => { + if (payload.type === 'update' || payload.type === 'full-reload') { + fs.appendFileSync(logFile, JSON.stringify(payload) + '\\n') + } + return send(payload) + } + } + }, + }, + ], + }) + `, + 'index.html': html` + + + + + + +
+ + + + `, + 'src/index.css': css`@import 'tailwindcss';`, + 'src/main.ts': ts` + import { createApp } from 'vue' + import App from './App.vue' + + createApp(App).mount('#app') + `, + 'src/App.vue': html` + + `, + + // This file is scanned by Tailwind but never imported, so it is not + // part of the loaded module graph (e.g. a lazy route that hasn't been + // visited yet) + 'src/LazyRoute.vue': html` + + `, + }, + }, + async ({ spawn, fs, expect }) => { + let process = await spawn('pnpm vite dev') + await process.onStdout((m) => m.includes('ready in')) + + let url = '' + await process.onStdout((m) => { + let match = /Local:\s*(http.*)\//.exec(m) + if (match) url = match[1] + return Boolean(url) + }) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['src/App.vue']`) + expect(styles).toContain(candidate`content-['src/LazyRoute.vue']`) + }) + + // Load `main.ts` and `App.vue` as real modules, like a browser visiting + // the page would + await fetch(`${url}/src/main.ts`) + await fetch(`${url}/src/App.vue`) + + // Changing the scanned but unloaded `.vue` file should not trigger a + // full reload, but new classes should still apply + await fs.write( + 'src/LazyRoute.vue', + html` + + `, + ) + + await retryAssertion(async () => { + let styles = await fetchStyles(url, '/index.html') + expect(styles).toContain(candidate`content-['updated:src/LazyRoute.vue']`) + }) + expect(await fs.read('hmr.log')).not.toContain('full-reload') + }, +) diff --git a/integrations/vitest.config.ts b/integrations/vitest.config.mts similarity index 100% rename from integrations/vitest.config.ts rename to integrations/vitest.config.mts diff --git a/integrations/webpack/index.test.ts b/integrations/webpack/index.test.ts new file mode 100644 index 000000000..2a91411d2 --- /dev/null +++ b/integrations/webpack/index.test.ts @@ -0,0 +1,110 @@ +import { css, html, js, json, test } from '../utils' + +test( + 'webpack + PostCSS (watch)', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "postcss": "^8", + "postcss-loader": "^7", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/postcss": "workspace:^" + } + } + `, + 'postcss.config.js': js` + /** @type {import('postcss-load-config').Config} */ + module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + }, + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + 'src/unrelated.module.css': css` + .module { + color: var(--color-blue-500); + } + `, + }, + }, + async ({ fs, spawn, exec, expect }) => { + // Generate the initial build so output CSS files exist on disk + await exec('pnpm webpack --mode=development') + + // NOTE: We are writing to an output CSS file which is not being ignored by + // `.gitignore` nor marked with `@source not`. This should not result in an + // infinite loop. + let process = await spawn('pnpm webpack --mode=development --watch') + await process.onStdout((m) => m.includes('compiled successfully in')) + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + :root, :host { + --color-blue-500: oklch(62.3% 0.214 259.815); + } + .flex { + display: flex; + } + " + `) + + await fs.write( + 'src/unrelated.module.css', + css` + .module { + color: var(--color-blue-500); + background-color: var(--color-red-500); + } + `, + ) + await process.onStdout((m) => m.includes('compiled successfully in')) + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + :root, :host { + --color-red-500: oklch(63.7% 0.237 25.331); + --color-blue-500: oklch(62.3% 0.214 259.815); + } + .flex { + display: flex; + } + " + `) + }, +) diff --git a/integrations/webpack/loader.test.ts b/integrations/webpack/loader.test.ts new file mode 100644 index 000000000..9c3d870af --- /dev/null +++ b/integrations/webpack/loader.test.ts @@ -0,0 +1,495 @@ +import { css, html, js, json, test } from '../utils' + +test( + '@tailwindcss/webpack loader (build)', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm webpack --mode=development') + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + .flex { + display: flex; + } + " + `) + }, +) + +test( + '@tailwindcss/webpack loader (watch)', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ fs, spawn, exec, expect }) => { + // Generate the initial build so output CSS files exist on disk + await exec('pnpm webpack --mode=development') + + let process = await spawn('pnpm webpack --mode=development --watch') + await process.onStdout((m) => m.includes('compiled successfully in')) + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + .flex { + display: flex; + } + " + `) + + // Add a new Tailwind class to the HTML file + await fs.write('src/index.html', html` +
+ `) + await process.onStdout((m) => m.includes('compiled successfully in')) + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + .flex { + display: flex; + } + .underline { + text-decoration-line: underline; + } + " + `) + }, +) + +test( + '@tailwindcss/webpack loader with @apply', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.css': css` + @import 'tailwindcss/theme'; + + .btn { + @apply flex items-center px-4 py-2 rounded-md; + } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm webpack --mode=development') + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + :root, :host { + --spacing: 0.25rem; + --radius-md: 0.375rem; + } + .btn { + display: flex; + align-items: center; + border-radius: var(--radius-md); + padding-inline: calc(var(--spacing) * 4); + padding-block: calc(var(--spacing) * 2); + } + " + `) + }, +) + +test( + '@tailwindcss/webpack loader with optimization', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: '@tailwindcss/webpack', + options: { + optimize: true, + }, + }, + ], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm webpack --mode=development') + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + .flex{display:flex} + " + `) + }, +) + +test( + '@tailwindcss/webpack loader with CSS @import', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import './custom.css'; + @import 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + 'src/custom.css': css` + /**/ + @utility custom-util { + color: var(--color-red-500); + } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm webpack --mode=development') + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + :root, :host { + --color-red-500: oklch(63.7% 0.237 25.331); + } + .flex { + display: flex; + } + .custom-util { + color: var(--color-red-500); + } + " + `) + }, +) + +test( + '@tailwindcss/webpack loader with @plugin', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + + module.exports = { + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack'], + }, + ], + }, + } + `, + 'src/index.js': js`import './index.css'`, + 'src/index.html': html` +
+ `, + 'src/index.css': css` + @import 'tailwindcss/utilities'; + @plugin './plugin.js'; + `, + 'src/plugin.js': js` + export default function ({ addUtilities }) { + addUtilities({ + '.custom-underline': { + 'border-bottom': '1px solid green', + }, + }) + } + `, + }, + }, + async ({ fs, exec, expect }) => { + await exec('pnpm webpack --mode=development') + + expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(` + " + --- ./dist/main.css --- + .custom-underline { + border-bottom: 1px solid green; + } + " + `) + }, +) + +test( + '@tailwindcss/webpack loader isolates cache by resource including query', + { + fs: { + 'package.json': json` + { + "main": "./src/index.js", + "browser": "./src/index.js", + "dependencies": { + "css-loader": "^6", + "webpack": "^5", + "webpack-cli": "^5", + "mini-css-extract-plugin": "^2", + "tailwindcss": "workspace:^", + "@tailwindcss/webpack": "workspace:^" + } + } + `, + 'webpack.config.js': js` + let MiniCssExtractPlugin = require('mini-css-extract-plugin') + let path = require('node:path') + + module.exports = { + mode: 'development', + entry: { + a: './src/a.js', + b: './src/b.js', + }, + output: { + clean: true, + }, + plugins: [new MiniCssExtractPlugin()], + module: { + rules: [ + { + test: /.css$/i, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + '@tailwindcss/webpack', + path.resolve(__dirname, 'query-loader.js'), + ], + }, + ], + }, + } + `, + 'query-loader.js': js` + module.exports = function (source) { + if (this.resourceQuery.includes('a')) { + return '@import "tailwindcss/utilities"; @utility only-a {color: var(--color-red-500);}' + } + + if (this.resourceQuery.includes('b')) { + return '@import "tailwindcss/utilities"; @utility only-b {color: var(--color-blue-500);}' + } + + return source + } + `, + 'src/a.js': js`import './index.css?a'`, + 'src/b.js': js`import './index.css?b'`, + 'src/index.css': css``, + }, + }, + async ({ fs, exec }) => { + await exec('pnpm webpack --mode=development') + + await fs.expectFileToContain('dist/a.css', ['only-a', '--color-red-500']) + await fs.expectFileToContain('dist/b.css', ['only-b', '--color-blue-500']) + await fs.expectFileNotToContain('dist/a.css', ['only-b', '--color-blue-500']) + await fs.expectFileNotToContain('dist/b.css', ['only-a', '--color-red-500']) + }, +) diff --git a/package.json b/package.json index 75befd109..7d41a469e 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "dev": "turbo dev --filter=!./playgrounds/*", "test": "cargo test && vitest run --hideSkippedTests", "test:integrations": "vitest --root=./integrations", - "test:ui": "pnpm run --filter=tailwindcss test:ui", + "test:ui": "pnpm run --filter=tailwindcss test:ui && pnpm run --filter=@tailwindcss/browser test:ui", "tdd": "vitest --hideSkippedTests", "bench": "vitest bench", "version-packages": "node ./scripts/version-packages.mjs", @@ -48,23 +48,17 @@ }, "license": "MIT", "devDependencies": { - "@playwright/test": "^1.49.0", + "@playwright/test": "^1.62.1", "@types/node": "catalog:", - "postcss": "8.4.49", - "postcss-import": "^16.1.0", - "prettier": "^3.3.3", - "prettier-plugin-embed": "^0.4.15", - "prettier-plugin-organize-imports": "^4.0.0", - "tsup": "^8.2.4", - "turbo": "^2.2.3", - "typescript": "^5.5.4", - "vitest": "^2.0.5" + "postcss": "catalog:", + "postcss-import": "^16.1.1", + "prettier": "catalog:", + "prettier-plugin-embed": "^0.5.1", + "prettier-plugin-organize-imports": "^4.3.0", + "tsup": "^8.5.1", + "turbo": "^2.10.8", + "typescript": "^5.9.3", + "vitest": "^4.1.10" }, - "packageManager": "pnpm@9.6.0", - "pnpm": { - "patchedDependencies": { - "lightningcss@1.26.0": "patches/lightningcss@1.26.0.patch", - "@parcel/watcher@2.5.0": "patches/@parcel__watcher@2.5.0.patch" - } - } + "packageManager": "pnpm@11.9.0" } diff --git a/packages/@tailwindcss-browser/README.md b/packages/@tailwindcss-browser/README.md new file mode 100644 index 000000000..5f532607d --- /dev/null +++ b/packages/@tailwindcss-browser/README.md @@ -0,0 +1,36 @@ +

+ + + + + Tailwind CSS + + +

+ +

+ A utility-first CSS framework for rapidly building custom user interfaces. +

+ +

+ Build Status + Total Downloads + Latest Release + License +

+ +--- + +## Documentation + +For full documentation, visit [tailwindcss.com](https://tailwindcss.com). + +## Community + +For help, discussion about best practices, or feature ideas: + +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) + +## Contributing + +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. diff --git a/packages/@tailwindcss-browser/package.json b/packages/@tailwindcss-browser/package.json new file mode 100644 index 000000000..70af78c1e --- /dev/null +++ b/packages/@tailwindcss-browser/package.json @@ -0,0 +1,37 @@ +{ + "name": "@tailwindcss/browser", + "version": "4.3.3", + "description": "A utility-first CSS framework for rapidly building custom user interfaces.", + "license": "MIT", + "main": "./dist/index.global.js", + "browser": "./dist/index.global.js", + "repository": { + "type": "git", + "url": "https://github.com/tailwindlabs/tailwindcss.git", + "directory": "packages/@tailwindcss-browser" + }, + "bugs": "https://github.com/tailwindlabs/tailwindcss/issues", + "homepage": "https://tailwindcss.com", + "scripts": { + "lint": "tsc --noEmit", + "build": "tsup-node", + "dev": "pnpm run build -- --watch", + "test:ui": "playwright test" + }, + "exports": { + ".": "./dist/index.global.js", + "./package.json": "./package.json" + }, + "files": [ + "dist" + ], + "publishConfig": { + "provenance": true, + "access": "public" + }, + "devDependencies": { + "h3": "^1.15.11", + "listhen": "^1.10.1", + "tailwindcss": "workspace:*" + } +} diff --git a/packages/@tailwindcss-browser/playwright.config.ts b/packages/@tailwindcss-browser/playwright.config.ts new file mode 100644 index 000000000..c6059dd82 --- /dev/null +++ b/packages/@tailwindcss-browser/playwright.config.ts @@ -0,0 +1,67 @@ +import { defineConfig, devices } from '@playwright/test' + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [['html', { open: 'never' }]], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + // Playwright 1.60's Windows WebKit worker hangs on shutdown after all tests + // pass, causing CI to fail with a worker force-kill error. + ...(process.platform === 'win32' + ? [] + : [{ name: 'webkit', use: { ...devices['Desktop Safari'] } }]), + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + // https://playwright.dev/docs/test-use-options#more-browser-and-context-options + launchOptions: { + // https://playwright.dev/docs/api/class-browsertype#browser-type-launch-option-firefox-user-prefs + firefoxUserPrefs: { + // By default, headless Firefox runs as though no pointers + // capabilities are available. + // https://github.com/microsoft/playwright/issues/7769#issuecomment-966098074 + // + // This impacts our `hover` variant implementation which uses an + // '(hover: hover)' media query to determine if hover is available. + // + // Available values for pointer capabilities: + // NO_POINTER = 0x00; + // COARSE_POINTER = 0x01; + // FINE_POINTER = 0x02; + // HOVER_CAPABLE_POINTER = 0x04; + // + // Setting to 0x02 | 0x04 says the system supports a mouse + 'ui.primaryPointerCapabilities': 0x02 | 0x04, + 'ui.allPointerCapabilities': 0x02 | 0x04, + }, + }, + }, + }, + ], +}) diff --git a/packages/@tailwindcss-browser/src/assets.ts b/packages/@tailwindcss-browser/src/assets.ts new file mode 100644 index 000000000..e72d47658 --- /dev/null +++ b/packages/@tailwindcss-browser/src/assets.ts @@ -0,0 +1,11 @@ +import index from 'tailwindcss/index.css' +import preflight from 'tailwindcss/preflight.css' +import theme from 'tailwindcss/theme.css' +import utilities from 'tailwindcss/utilities.css' + +export const css = { + index, + preflight, + theme, + utilities, +} diff --git a/packages/@tailwindcss-browser/src/index.ts b/packages/@tailwindcss-browser/src/index.ts new file mode 100644 index 000000000..49c7e4675 --- /dev/null +++ b/packages/@tailwindcss-browser/src/index.ts @@ -0,0 +1,294 @@ +import * as tailwindcss from 'tailwindcss' +import * as assets from './assets' +import { Instrumentation } from './instrumentation' + +/** + * The type used by ` + `, + body: html`
`, + }) + + await expect(page.locator('[data-test]')).toHaveCSS('background-color', 'rgb(255, 0, 0)') + + await page.evaluate(() => { + document.querySelector('[data-css]')!.textContent = ` + .foo { + @apply bg-blue; + } + ` + }) + + await expect(page.locator('[data-test]')).toHaveCSS('background-color', 'rgb(0, 0, 255)') +}) + +test('changes to `@theme`', async ({ page }) => { + await server.render({ + page, + head: html` + + `, + body: html`
`, + }) + + await expect(page.locator('[data-test]')).toHaveCSS('background-color', 'rgb(255, 0, 0)') + + await page.evaluate(() => { + document.querySelector('[data-css]')!.textContent = ` + @theme { + --color-primary: #0000ff; + } + ` + }) + + await expect(page.locator('[data-test]')).toHaveCSS('background-color', 'rgb(0, 0, 255)') +}) + +test('no classes', async ({ page }) => { + await server.render({ + page, + body: html`
test
`, + }) + + await expect(page.locator('body')).toHaveCSS('margin', '0px') +}) + +test('html classes', async ({ page }) => { + await server.render({ + page, + htmlClasses: 'h-4', + }) + + await expect(page.locator('html')).toHaveCSS('height', '16px') +}) + +async function createServer() { + const { createApp, createRouter, defineEventHandler, toNodeListener } = await import('h3') + const { listen } = await import('listhen') + + interface PageOptions { + page: Page + head?: string + body?: string + htmlClasses?: string + } + + async function render({ page, htmlClasses, head, body }: PageOptions) { + let content = html` + + + + + + + Document + + + ${head ?? ''} + + + ${body ?? ''} + + + ` + + router.get( + '/', + defineEventHandler(() => content), + ) + + await page.goto(server.url) + } + + const app = createApp() + const router = createRouter() + + router.get( + '/tailwindcss.js', + defineEventHandler(() => readFile(require.resolve('@tailwindcss/browser'))), + ) + + app.use(router) + + let workerIndex = Number(process.env.TEST_WORKER_INDEX ?? 0) + + let listener = await listen(toNodeListener(app), { + port: 3000 + workerIndex, + showURL: false, + open: false, + }) + + return { + app, + url: listener.url, + close: listener.close, + render, + } +} diff --git a/packages/@tailwindcss-browser/tsconfig.json b/packages/@tailwindcss-browser/tsconfig.json new file mode 100644 index 000000000..8ea040daf --- /dev/null +++ b/packages/@tailwindcss-browser/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "lib": ["es2022", "esnext.disposable", "dom", "dom.iterable"], + }, +} diff --git a/packages/@tailwindcss-browser/tsup.config.ts b/packages/@tailwindcss-browser/tsup.config.ts new file mode 100644 index 000000000..10a73f2b7 --- /dev/null +++ b/packages/@tailwindcss-browser/tsup.config.ts @@ -0,0 +1,32 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + format: ['iife'], + clean: true, + minify: true, + entry: ['src/index.ts'], + noExternal: [/.*/], + loader: { + '.css': 'text', + }, + define: { + 'process.env.NODE_ENV': '"production"', + 'process.env.FEATURES_ENV': '"stable"', + }, + esbuildPlugins: [ + { + name: 'patch-intellisense-apis', + setup(build) { + build.onLoad({ filter: /intellisense.ts$/ }, () => { + return { + contents: ` + export function getClassList() { return [] } + export function getVariants() { return [] } + export function canonicalizeCandidates() { return [] } + `, + } + }) + }, + }, + ], +}) diff --git a/packages/@tailwindcss-browser/vitest.config.mts b/packages/@tailwindcss-browser/vitest.config.mts new file mode 100644 index 000000000..f6f91227d --- /dev/null +++ b/packages/@tailwindcss-browser/vitest.config.mts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + exclude: ['**/*.spec.?(c|m)[jt]s?(x)', '**/node_modules/**'], + }, +}) diff --git a/packages/@tailwindcss-cli/README.md b/packages/@tailwindcss-cli/README.md index 95ec9d87d..5f532607d 100644 --- a/packages/@tailwindcss-cli/README.md +++ b/packages/@tailwindcss-cli/README.md @@ -13,10 +13,10 @@

- Build Status + Build Status Total Downloads - Latest Release - License + Latest Release + License

--- @@ -27,14 +27,10 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com). ## Community -For help, discussion about best practices, or any other conversation that would benefit from being searchable: +For help, discussion about best practices, or feature ideas: -[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) - -For chatting with others using the framework: - -[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe) +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) ## Contributing -If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. diff --git a/packages/@tailwindcss-cli/package.json b/packages/@tailwindcss-cli/package.json index 4a71f0724..7fa5b5500 100644 --- a/packages/@tailwindcss-cli/package.json +++ b/packages/@tailwindcss-cli/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/cli", - "version": "4.0.0-beta.7", + "version": "4.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { @@ -29,11 +29,10 @@ "access": "public" }, "dependencies": { - "@parcel/watcher": "^2.5.0", - "@tailwindcss/node": "workspace:^", - "@tailwindcss/oxide": "workspace:^", - "enhanced-resolve": "^5.17.1", - "lightningcss": "catalog:", + "@parcel/watcher": "catalog:", + "@tailwindcss/node": "workspace:*", + "@tailwindcss/oxide": "workspace:*", + "enhanced-resolve": "catalog:", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "workspace:*" diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index bf1066e90..ffef2ae8b 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -1,8 +1,13 @@ -import watcher from '@parcel/watcher' -import { compile, env, Instrumentation } from '@tailwindcss/node' +import { + compile, + env, + Instrumentation, + optimize, + toSourceMap, + type SourceMap, +} from '@tailwindcss/node' import { clearRequireCache } from '@tailwindcss/node/require-cache' import { Scanner, type ChangedContent } from '@tailwindcss/oxide' -import { Features, transform } from 'lightningcss' import { existsSync, type Stats } from 'node:fs' import fs from 'node:fs/promises' import path from 'node:path' @@ -15,11 +20,13 @@ import { highlight, println, relative, + wordWrap, } from '../../utils/renderer' import { drainStdin, outputFile } from './utils' const css = String.raw const DEBUG = env.DEBUG +const DEFAULT_POLL_INTERVAL_MS = 250 export function options() { return { @@ -32,11 +39,20 @@ export function options() { type: 'string', description: 'Output file', alias: '-o', + default: '-', }, '--watch': { type: 'boolean | string', - description: 'Watch for changes and rebuild as needed', + description: + 'Watch for changes and rebuild as needed, and use `always` to keep watching when stdin is closed', alias: '-w', + values: ['always'], + }, + '--poll': { + type: 'boolean | number', + description: 'Use polling instead of filesystem events when watching', + default: false, + values: ['ms'], }, '--minify': { type: 'boolean', @@ -52,28 +68,73 @@ export function options() { description: 'The current working directory', default: '.', }, + '--map': { + type: 'boolean | string', + description: 'Generate a source map', + default: false, + }, + '--silent': { + type: 'boolean', + description: 'Suppress non-error output', + }, } satisfies Arg } +function formatError(error: unknown): string { + let seen = new Set() // Track seen errors to prevent circular errors + function render(err: unknown, depth: number): string[] { + let indent = ' '.repeat(depth) + let width = (process.stderr.columns ?? Infinity) - 2 * (depth + 1) + + let output = [ + `${indent}${red(depth === 0 ? 'Error:' : 'Caused by:')}`, + `${indent}${dim('\u250C')}`, + ] + + for (let line of `${err}`.split('\n')) { + let wrapped = wordWrap(line, width) + if (wrapped.length === 0) wrapped = [''] + for (let chunk of wrapped) { + output.push(`${indent}${dim('\u2502')} ${chunk}`) + } + } + + output.push(`${indent}${dim('\u2514')}`) + + if (typeof err === 'object' && err !== null && 'cause' in err && err.cause != null) { + if (!seen.add(err).has(err.cause)) { + output.push(...render(err.cause, depth + 1)) + } + } + + return output + } + + return render(error, 0).join('\n') +} + async function handleError(fn: () => T): Promise { try { return await fn() } catch (err) { - if (err instanceof Error) { - eprintln(err.toString()) - } + eprintln(formatError(err)) process.exit(1) } } export async function handle(args: Result>) { + if (!args['--silent']) eprintln(header()) + if (!args['--silent']) eprintln() + using I = new Instrumentation() DEBUG && I.start('[@tailwindcss/cli] (initial build)') let base = path.resolve(args['--cwd']) - // Resolve the output as an absolute path. - if (args['--output']) { + // Resolve the output as an absolute path. If the output is a `-`, then we + // don't need to resolve it because this is a flag to indicate that we want to + // use `stdout` instead. + if (args['--output'] && args['--output'] !== '-') { args['--output'] = path.resolve(base, args['--output']) } @@ -85,13 +146,43 @@ export async function handle(args: Result>) { // Ensure the provided `--input` exists. if (!existsSync(args['--input'])) { - eprintln(header()) - eprintln() eprintln(`Specified input file ${highlight(relative(args['--input']))} does not exist.`) process.exit(1) } } + // Check if the input and output file paths are identical, otherwise return an + // error to the user. + if (args['--input'] === args['--output'] && args['--input'] !== '-') { + eprintln( + `Specified input file ${highlight(relative(args['--input']))} and output file ${highlight(relative(args['--output']))} are identical.`, + ) + process.exit(1) + } + + // If the user passes `{bin} build --map -` then this likely means they want to output the map inline + // this is the default behavior of `{bin build} --map` to inform the user of that + if (args['--map'] === '-') { + eprintln(`Use --map without a value to inline the source map.`) + process.exit(1) + } + + if (args['--poll'] === undefined) { + eprintln(`Use --poll with a non-zero value in milliseconds.`) + process.exit(1) + } + let pollInterval = args['--poll'] === true ? DEFAULT_POLL_INTERVAL_MS : args['--poll'] + if (pollInterval !== false && pollInterval <= 0) { + eprintln(`Specified polling interval must be a positive number.`) + process.exit(1) + } + + // Resolve the map as an absolute path. If the output is true then we + // don't need to resolve it because it'll be an inline source map + if (args['--map'] && args['--map'] !== true) { + args['--map'] = path.resolve(base, args['--map']) + } + let start = process.hrtime.bigint() let input = args['--input'] @@ -105,36 +196,75 @@ export async function handle(args: Result>) { let previous = { css: '', optimizedCss: '', + output: null as string | null, } - async function write(css: string, args: Result>, I: Instrumentation) { + async function write( + css: string, + map: SourceMap | null, + args: Result>, + I: Instrumentation, + ) { let output = css // Optimize the output if (args['--minify'] || args['--optimize']) { if (css !== previous.css) { DEBUG && I.start('Optimize CSS') - let optimizedCss = optimizeCss(css, { + let optimized = optimize(css, { file: args['--input'] ?? 'input.css', minify: args['--minify'] ?? false, + map: map?.raw ?? undefined, }) DEBUG && I.end('Optimize CSS') previous.css = css - previous.optimizedCss = optimizedCss - output = optimizedCss + previous.optimizedCss = optimized.code + if (optimized.map) { + map = toSourceMap(optimized.map) + } + output = optimized.code } else { output = previous.optimizedCss } } // Write the output + if (map) { + // Inline the source map + if (args['--map'] === true) { + output += `\n` + output += map.inline + } else if (typeof args['--map'] === 'string') { + let basePath = + args['--output'] && args['--output'] !== '-' + ? path.dirname(path.resolve(args['--output'])) + : process.cwd() + + let mapPath = path.resolve(args['--map']) + + let relativePath = path.relative(basePath, mapPath) + + output += `\n` + output += map.comment(relativePath) + + DEBUG && I.start('Write source map') + await outputFile(args['--map'], map.raw) + DEBUG && I.end('Write source map') + } + } + + let didChange = output !== previous.output + DEBUG && I.start('Write output') - if (args['--output']) { + if (args['--output'] && args['--output'] !== '-') { await outputFile(args['--output'], output) - } else { + } else if (didChange) { println(output) } DEBUG && I.end('Write output') + + previous.output = output + return didChange } let inputFilePath = @@ -143,10 +273,12 @@ export async function handle(args: Result>) { let inputBasePath = inputFilePath ? path.dirname(inputFilePath) : process.cwd() let fullRebuildPaths: string[] = inputFilePath ? [inputFilePath] : [] + let backupRebuildPaths = fullRebuildPaths async function createCompiler(css: string, I: Instrumentation) { DEBUG && I.start('Setup compiler') let compiler = await compile(css, { + from: args['--output'] ? (inputFilePath ?? 'stdin.css') : undefined, base: inputBasePath, onDependency(path) { fullRebuildPaths.push(path) @@ -161,12 +293,30 @@ export async function handle(args: Result>) { // No root specified, use the base directory if (compiler.root === null) { - return [{ base, pattern: '**/*' }] + return [{ base, pattern: '**/*', negated: false }] } // Use the specified root - return [compiler.root] - })().concat(compiler.globs) + return [{ ...compiler.root, negated: false }] + })().concat(compiler.sources) + + // Do not scan the current executable. Otherwise when using the standalone + // CLI, if the CLI lives in the current repo we would be scanning that file. + // + // This is also immune against renames of the executable file. + sources.push({ + base: path.dirname(process.execPath), + pattern: path.basename(process.execPath), + negated: true, + }) + + if (inputFilePath !== null) { + sources.push({ + base: path.dirname(inputFilePath), + pattern: path.basename(inputFilePath), + negated: false, + }) + } let scanner = new Scanner({ sources }) DEBUG && I.end('Setup compiler') @@ -175,12 +325,16 @@ export async function handle(args: Result>) { } let [compiler, scanner] = await handleError(() => createCompiler(input, I)) + let cleanupWatchers: (() => Promise)[] = [] // Watch for changes - if (args['--watch']) { - let cleanupWatchers = await createWatchers( - watchDirectories(scanner), - async function handle(files) { + if (args['--watch'] && pollInterval === false) { + // Ensure the file watcher can be loaded before setting up any watchers, + // such that we can present a helpful error message if needed. + await handleError(() => loadWatcher()) + + cleanupWatchers.push( + await createWatchers(await watchDirectories(scanner), async function handle(files) { try { // If the only change happened to the output file, then we don't want to // trigger a rebuild because that will result in an infinite loop. @@ -192,35 +346,15 @@ export async function handle(args: Result>) { // Re-compile the input let start = process.hrtime.bigint() - let changedFiles: ChangedContent[] = [] - let rebuildStrategy: 'incremental' | 'full' = 'incremental' - let resolvedFullRebuildPaths = fullRebuildPaths - - for (let file of files) { - // If one of the changed files is related to the input CSS or JS - // config/plugin files, then we need to do a full rebuild because - // the theme might have changed. - if (resolvedFullRebuildPaths.includes(file)) { - rebuildStrategy = 'full' - - // No need to check the rest of the events, because we already know we - // need to do a full rebuild. - break - } - - // Track new and updated files for incremental rebuilds. - changedFiles.push({ - file, - extension: path.extname(file).slice(1), - } satisfies ChangedContent) - } + let rebuildStrategy = getRebuildStrategy(files, resolvedFullRebuildPaths) // Track the compiled CSS let compiledCss = '' + let compiledMap: SourceMap | null = null // Scan the entire `base` directory for full rebuilds. - if (rebuildStrategy === 'full') { + if (rebuildStrategy.kind === 'full') { // Read the new `input`. let input = args['--input'] ? args['--input'] === '-' @@ -230,11 +364,23 @@ export async function handle(args: Result>) { @import 'tailwindcss'; ` clearRequireCache(resolvedFullRebuildPaths) + + // Track current rebuild paths in case something goes wrong when + // performing a full rebuild. + backupRebuildPaths = fullRebuildPaths.slice() + + // The `inputFilePath`, if provided, will be the only known full + // rebuild path before the compiler is re-created. fullRebuildPaths = inputFilePath ? [inputFilePath] : [] // Create a new compiler, given the new `input` ;[compiler, scanner] = await createCompiler(input, I) + // Successfully created a new compiler, so the `fullRebuildPaths` + // will be updated. If other errors occur, we should be able to + // restore the paths unconditionally. + backupRebuildPaths = fullRebuildPaths.slice() + // Scan the directory for candidates DEBUG && I.start('Scan for candidates') let candidates = scanner.scan() @@ -242,60 +388,107 @@ export async function handle(args: Result>) { // Setup new watchers DEBUG && I.start('Setup new watchers') - let newCleanupWatchers = await createWatchers(watchDirectories(scanner), handle) + let newCleanupFunction = await createWatchers(await watchDirectories(scanner), handle) DEBUG && I.end('Setup new watchers') // Clear old watchers DEBUG && I.start('Cleanup old watchers') - await cleanupWatchers() + await Promise.all(cleanupWatchers.splice(0).map((cleanup) => cleanup())) DEBUG && I.end('Cleanup old watchers') - cleanupWatchers = newCleanupWatchers + cleanupWatchers.push(newCleanupFunction) // Re-compile the CSS DEBUG && I.start('Build CSS') compiledCss = compiler.build(candidates) DEBUG && I.end('Build CSS') + + if (args['--map']) { + DEBUG && I.start('Build Source Map') + compiledMap = toSourceMap(compiler.buildSourceMap()) + DEBUG && I.end('Build Source Map') + } } // Scan changed files only for incremental rebuilds. - else if (rebuildStrategy === 'incremental') { + else if (rebuildStrategy.kind === 'incremental') { DEBUG && I.start('Scan for candidates') - let newCandidates = scanner.scanFiles(changedFiles) + let newCandidates = scanner.scanFiles(rebuildStrategy.changedFiles) DEBUG && I.end('Scan for candidates') // No new candidates found which means we don't need to write to // disk, and can return early. if (newCandidates.length <= 0) { let end = process.hrtime.bigint() - eprintln(`Done in ${formatDuration(end - start)}`) + if (!args['--silent']) eprintln(`Done in ${formatDuration(end - start)}`) return } DEBUG && I.start('Build CSS') compiledCss = compiler.build(newCandidates) DEBUG && I.end('Build CSS') + + if (args['--map']) { + DEBUG && I.start('Build Source Map') + compiledMap = toSourceMap(compiler.buildSourceMap()) + DEBUG && I.end('Build Source Map') + } } - await write(compiledCss, args, I) + await write(compiledCss, compiledMap, args, I) let end = process.hrtime.bigint() - eprintln(`Done in ${formatDuration(end - start)}`) + if (!args['--silent']) eprintln(`Done in ${formatDuration(end - start)}`) } catch (err) { + // It's important that we perform a full rebuild when any of the + // dependencies tracked in `fullRebuildPaths` has been changed. + // + // If we remove one of those files, then a subsequent build will be + // triggered, but it will fail because the dependency is gone. The + // compiler itself will be in a broken state and won't be able to + // register any dependencies therefore we want to restore all the + // dependencies from before. If we don't do that, then we won't be + // able to recover from a bug in a transitive dependency. + // + // E.g.: + // ```css + // /* input.css — known full rebuild path */ + // @import 'tailwindcss'; + // @config "./tailwind.config.js"; + // ``` + // + // ```js + // // tailwind.config.js + // const theme = require('./my-theme.js'); + // + // module.exports = { + // theme + // } + // ``` + // In this case `my-theme.js` is a transitive dependency of + // `input.css` via `tailwind.config.js`. Removing `my-theme.js` will + // result in an error, restoring `my-theme.js` should trigger a + // fresh build even though the compiler didn't restore. + // + // Once the build error is fixed, a fresh full rebuild will happen + // which in turn will fixup the full rebuild paths. + fullRebuildPaths = backupRebuildPaths + // Catch any errors and print them to stderr, but don't exit the process // and keep watching. - if (err instanceof Error) { - eprintln(err.toString()) - } + eprintln(formatError(err)) + + let end = process.hrtime.bigint() + if (!args['--silent']) eprintln(`Done in ${formatDuration(end - start)}`) } - }, + }), ) // 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', () => { - cleanupWatchers().then( + Promise.all(cleanupWatchers.map((fn) => fn())).then( () => process.exit(0), () => process.exit(1), ) @@ -312,27 +505,197 @@ export async function handle(args: Result>) { DEBUG && I.start('Build CSS') let output = await handleError(() => compiler.build(candidates)) DEBUG && I.end('Build CSS') - await write(output, args, I) + + let map: SourceMap | null = null + + if (args['--map']) { + DEBUG && I.start('Build Source Map') + map = await handleError(() => toSourceMap(compiler.buildSourceMap())) + DEBUG && I.end('Build Source Map') + } + + await write(output, map, args, I) let end = process.hrtime.bigint() - eprintln(header()) - eprintln() - eprintln(`Done in ${formatDuration(end - start)}`) + if (!args['--silent']) eprintln(`Done in ${formatDuration(end - start)}`) + + // Watch for changes, using polling + if (args['--watch'] && pollInterval !== false) { + let cleanupPollingIndicator = () => {} + + function logPollingMessage(message: string) { + if (!args['--silent']) { + process.stderr.write('\r\x1B[2K') + } + + eprintln(message) + + if (!args['--silent']) { + process.stderr.write(`\r\x1B[2K${dim(`Polling for changes…`)}`) + } + } + + async function fullRebuild(I: Instrumentation) { + clearRequireCache(fullRebuildPaths) + backupRebuildPaths = fullRebuildPaths.slice() + fullRebuildPaths = inputFilePath ? [inputFilePath] : [] + + let input = args['--input'] + ? args['--input'] === '-' + ? await drainStdin() + : await fs.readFile(args['--input'], 'utf-8') + : css` + @import 'tailwindcss'; + ` + + ;[compiler, scanner] = await createCompiler(input, I) + backupRebuildPaths = fullRebuildPaths.slice() + + DEBUG && I.start('Scan for candidates') + let candidates = scanner.scan() + DEBUG && I.end('Scan for candidates') + + DEBUG && I.start('Build CSS') + let output = compiler.build(candidates) + DEBUG && I.end('Build CSS') + + let map: SourceMap | null = null + + if (args['--map']) { + DEBUG && I.start('Build Source Map') + map = toSourceMap(compiler.buildSourceMap()) + DEBUG && I.end('Build Source Map') + } + + await write(output, map, args, I) + } + + if (!args['--silent']) { + let restored = false + + function restoreCursor() { + if (restored) return + restored = true + process.stderr.write('\r\x1B[2K\x1B[?25h') + process.off('exit', restoreCursor) + process.off('SIGINT', onSigint) + process.off('SIGTERM', onSigterm) + } + + function onSigint() { + restoreCursor() + process.exit(130) + } + + function onSigterm() { + restoreCursor() + process.exit(143) + } + + process.stderr.write('\x1B[?25l') + process.on('exit', restoreCursor) + process.on('SIGINT', onSigint) + process.on('SIGTERM', onSigterm) + + cleanupPollingIndicator = restoreCursor + cleanupWatchers.push(async () => cleanupPollingIndicator()) + } + + cleanupWatchers.push( + createPollingWatcher(async () => { + using I = new Instrumentation() + + DEBUG && I.start('Scan for candidates') + let candidates = scanner.scan() + DEBUG && I.end('Scan for candidates') + + let files = scanner.scannedFiles.filter( + (file) => file !== args['--output'] && file !== args['--map'], + ) + + if (files.length <= 0) return + + let start = process.hrtime.bigint() + + let strategy = getRebuildStrategy(files, fullRebuildPaths) + + if (strategy.kind === 'full') { + try { + await fullRebuild(I) + } catch (err) { + fullRebuildPaths = backupRebuildPaths + + logPollingMessage(formatError(err)) + } + + if (!args['--silent']) { + logPollingMessage(`Done in ${formatDuration(process.hrtime.bigint() - start)}`) + } + return + } + + // No candidates found which means we don't need to write to disk, and + // can return early. + if (candidates.length <= 0) { + if (!args['--silent']) { + logPollingMessage(`Done in ${formatDuration(process.hrtime.bigint() - start)}`) + } + return + } + + DEBUG && I.start('Build CSS') + let output = compiler.build(candidates) + DEBUG && I.end('Build CSS') + + let map: SourceMap | null = null + + if (args['--map']) { + DEBUG && I.start('Build Source Map') + map = toSourceMap(compiler.buildSourceMap()) + DEBUG && I.end('Build Source Map') + } + + await write(output, map, args, I) + + if (!args['--silent']) { + logPollingMessage(`Done in ${formatDuration(process.hrtime.bigint() - start)}`) + } + }, pollInterval), + ) + + if (args['--watch'] !== 'always') { + process.stdin.on('end', () => { + Promise.all(cleanupWatchers.map((fn) => fn())).then( + () => process.exit(0), + () => process.exit(1), + ) + }) + } + + process.stdin.resume() + if (!args['--silent']) { + process.stderr.write(`\r\x1B[2K${dim(`Polling for changes…`)}`) + } + } } -function watchDirectories(scanner: Scanner) { - return scanner.globs.flatMap((globEntry) => { - // We don't want a watcher for negated globs. - if (globEntry.pattern[0] === '!') return [] - - // We don't want a watcher for files, only directories. - if (globEntry.pattern === '') return [] - - return globEntry.base - }) +// Load `@parcel/watcher` lazily so a missing or broken native binding only +// affects `--watch` (without `--poll`), instead of crashing one-off builds and +// polling mode as well. +async function loadWatcher(): Promise { + try { + return (await import('@parcel/watcher')).default + } catch (err) { + throw new Error( + `Failed to load the file watcher. Your platform may not be supported by \`@parcel/watcher\`. As a workaround, you can use polling instead by passing the \`--watch --poll\` flags.`, + { cause: err }, + ) + } } async function createWatchers(dirs: string[], cb: (files: string[]) => void) { + let watcher = await loadWatcher() + // Remove any directories that are children of an already watched directory. // If we don't we may not get notified of certain filesystem events regardless // of whether or not they are for the directory that is duplicated. @@ -394,10 +757,16 @@ async function createWatchers(dirs: string[], cb: (files: string[]) => void) { await Promise.all( events.map(async (event) => { - // We currently don't handle deleted files because it doesn't influence - // the CSS output. This is because we currently keep all scanned - // candidates in a cache for performance reasons. - if (event.type === 'delete') return + // When a file is deleted, a rebuild should be triggered such that we + // can figure out whether this file must trigger a fresh build or not. + // + // If it must trigger a fresh build, then we will temporarily end up + // in a broken state, but an error will be shown to the user. Once the + // user resolves the issue, the CLI will recover. + if (event.type === 'delete') { + files.add(event.path) + return + } // Ignore directory changes. We only care about file changes let stats: Stats | null = null @@ -428,35 +797,78 @@ async function createWatchers(dirs: string[], cb: (files: string[]) => void) { } } -function optimizeCss( - input: string, - { file = 'input.css', minify = false }: { file?: string; minify?: boolean } = {}, -) { - function optimize(code: Buffer | Uint8Array) { - return transform({ - filename: file, - code, - minify, - sourceMap: false, - drafts: { - customMedia: true, - }, - nonStandard: { - deepSelectorCombinator: true, - }, - include: Features.Nesting, - exclude: Features.LogicalProperties | Features.DirSelector, - targets: { - safari: (16 << 16) | (4 << 8), - ios_saf: (16 << 16) | (4 << 8), - firefox: 128 << 16, - chrome: 111 << 16, - }, - errorRecovery: true, - }).code +function getRebuildStrategy( + files: string[], + fullRebuildPaths: string[], +): { kind: 'incremental'; changedFiles: ChangedContent[] } | { kind: 'full' } { + let changedFiles: ChangedContent[] = [] + + for (let file of files) { + // If one of the changed files is related to the input CSS or JS + // config/plugin files, then we need to do a full rebuild because + // the theme might have changed. + if (fullRebuildPaths.includes(file)) { + return { kind: 'full' } + } + + // Track new and updated files for incremental rebuilds. + changedFiles.push({ + file, + extension: path.extname(file).slice(1), + } satisfies ChangedContent) } - // Running Lightning CSS twice to ensure that adjacent rules are merged after - // nesting is applied. This creates a more optimized output. - return optimize(optimize(Buffer.from(input))).toString() + return { kind: 'incremental', changedFiles } +} + +function createPollingWatcher(cb: () => Promise, pollInterval: number) { + let disposed = false + let timer: ReturnType | null = null + + async function poll() { + if (disposed) return + + try { + await cb() + } catch (err) { + console.error(err) + } finally { + if (!disposed) { + timer = setTimeout(poll, pollInterval) + } + } + } + + timer = setTimeout(poll, pollInterval) + + return async () => { + disposed = true + if (timer) clearTimeout(timer) + } +} + +async function watchDirectories(scanner: Scanner) { + let directories = ( + await Promise.all( + scanner.normalizedSources.map(async (globEntry) => { + let resolvedPath = path.resolve(globEntry.base) + let realPath = await fs.realpath(resolvedPath).catch(() => resolvedPath) + + return fs + .stat(realPath) + .then((stat) => (stat.isDirectory() ? [realPath] : [])) + .catch(() => []) + }), + ) + ).flat(1) + + return Array.from(new Set(directories)) +} + +function dim(str: string) { + return `\x1B[2m${str}\x1B[22m` +} + +function red(str: string) { + return `\x1B[31m${str}\x1B[39m` } diff --git a/packages/@tailwindcss-cli/src/commands/build/utils.ts b/packages/@tailwindcss-cli/src/commands/build/utils.ts index b41a74358..ecdd96726 100644 --- a/packages/@tailwindcss-cli/src/commands/build/utils.ts +++ b/packages/@tailwindcss-cli/src/commands/build/utils.ts @@ -13,10 +13,19 @@ export function drainStdin() { } export async function outputFile(file: string, contents: string) { - try { - let currentContents = await fs.readFile(file, 'utf8') - if (currentContents === contents) return // Skip writing the file - } catch {} + // Check for special files like `/dev/stdout` or pipes. We don't want to read from these as that + // will hang the process until the file descriptors are closed. + let isSpecialFile = await fs + .stat(file) + .then((stats) => stats.isCharacterDevice() || stats.isFIFO()) + .catch(() => false) + + if (!isSpecialFile) { + try { + let currentContents = await fs.readFile(file, 'utf8') + if (currentContents === contents) return // Skip writing the file + } catch {} + } // Ensure the parent directories exist await fs.mkdir(path.dirname(file), { recursive: true }) diff --git a/packages/@tailwindcss-cli/src/commands/canonicalize/canonicalize.test.ts b/packages/@tailwindcss-cli/src/commands/canonicalize/canonicalize.test.ts new file mode 100644 index 000000000..4392380a5 --- /dev/null +++ b/packages/@tailwindcss-cli/src/commands/canonicalize/canonicalize.test.ts @@ -0,0 +1,175 @@ +import path from 'node:path' +import { PassThrough, Readable } from 'node:stream' +import { fileURLToPath } from 'node:url' +import { describe, expect, test } from 'vitest' +import { runCommandLine, streamStdin } from '.' +import { normalizeWindowsSeparators } from '../../utils/test-helpers' + +let css = normalizeWindowsSeparators( + path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'fixtures/input.css'), +) + +describe('runCommandLine', { timeout: 30_000 }, () => { + test('canonicalizes, collapses, and sorts candidate groups from positional arguments', async () => { + let result = await runCommandLine({ + argv: ['--css', css, 'py-3 p-1 px-3'], + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result).toEqual({ + exitCode: 0, + stdout: 'p-3', + stderr: '', + }) + }) + + test('falls back to the default tailwind import when --css is omitted', async () => { + let result = await runCommandLine({ + argv: ['py-3 p-1 px-3'], + cwd: path.dirname(css), + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result).toEqual({ + exitCode: 0, + stdout: 'p-3', + stderr: '', + }) + }) + + test('canonicalizes, collapses, and sorts multiple groups from stdin lines', async () => { + let result = await runCommandLine({ + argv: ['--css', css], + stdin: '[display:_flex_] py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2 focus:hover:p-3 hover:p-1 py-3\n', + stdinIsTTY: false, + stdoutIsTTY: false, + }) + + expect(result).toEqual({ + exitCode: 0, + stdout: 'flex p-3\nm-2 py-3 hover:p-1 focus:hover:p-3', + stderr: '', + }) + }) + + test('collapses equivalent candidates', async () => { + let result = await runCommandLine({ + argv: ['--css', css, 'mt-2 mr-2 mb-2 ml-2'], + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result).toEqual({ + exitCode: 0, + stdout: 'm-2', + stderr: '', + }) + }) + + test('renders json output for processed candidate groups', async () => { + let result = await runCommandLine({ + argv: ['--css', css, '--format', 'json', 'py-3 p-1 px-3'], + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).toEqual([ + { + input: 'py-3 p-1 px-3', + output: 'p-3', + changed: true, + }, + ]) + expect(result.stderr).toBe('') + }) + + test('splits candidate lists with segment-aware spacing', async () => { + let result = await runCommandLine({ + argv: ['--css', css, '--format', 'json', "content-['hello world'] p-1"], + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result.exitCode).toBe(0) + expect(JSON.parse(result.stdout)).toEqual([ + { + input: "content-['hello world'] p-1", + output: "p-1 content-['hello_world']", + changed: true, + }, + ]) + expect(result.stderr).toBe('') + }) + + test('shows a usage error when no candidate groups are provided', async () => { + let result = await runCommandLine({ + argv: ['--css', css], + stdinIsTTY: true, + stdoutIsTTY: false, + }) + + expect(result.exitCode).toBe(1) + expect(result.stderr).toBe('No candidate groups provided') + expect(result.stdout).toContain('Usage:') + }) + + test('streams canonicalized output line by line', async () => { + let input = Readable.from('py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2\n') + let { stream: output, collect: collectOutput } = createOutput() + + await streamStdin({ css, cwd: path.dirname(css), format: 'text', input, output }) + + expect(collectOutput()).toBe('p-3\nm-2\n') + }) + + test('streams empty lines as empty lines', async () => { + let input = Readable.from('py-3 p-1 px-3\n\nmt-2 mr-2 mb-2 ml-2\n') + let { stream: output, collect: collectOutput } = createOutput() + + await streamStdin({ css, cwd: path.dirname(css), format: 'text', input, output }) + + expect(collectOutput()).toBe('p-3\n\nm-2\n') + }) + + test('streams json output when requested', async () => { + let input = Readable.from('py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2\n') + let { stream: output, collect: collectOutput } = createOutput() + + await streamStdin({ css, cwd: path.dirname(css), format: 'json', input, output }) + + expect(JSON.parse(collectOutput())).toEqual([ + { + input: 'py-3 p-1 px-3', + output: 'p-3', + changed: true, + }, + { + input: 'mt-2 mr-2 mb-2 ml-2', + output: 'm-2', + changed: true, + }, + ]) + }) + + test('streams jsonl output when requested', async () => { + let input = Readable.from('py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2\n') + let { stream: output, collect: collectOutput } = createOutput() + + await streamStdin({ css, cwd: path.dirname(css), format: 'jsonl', input, output }) + + expect(collectOutput()).toBe( + '{"input":"py-3 p-1 px-3","output":"p-3","changed":true}\n' + + '{"input":"mt-2 mr-2 mb-2 ml-2","output":"m-2","changed":true}\n', + ) + }) +}) + +function createOutput() { + let stream = new PassThrough() + let chunks: Buffer[] = [] + stream.on('data', (chunk) => chunks.push(chunk)) + return { stream, collect: () => Buffer.concat(chunks).toString() } +} diff --git a/packages/@tailwindcss-cli/src/commands/canonicalize/fixtures/input.css b/packages/@tailwindcss-cli/src/commands/canonicalize/fixtures/input.css new file mode 100644 index 000000000..d4b507858 --- /dev/null +++ b/packages/@tailwindcss-cli/src/commands/canonicalize/fixtures/input.css @@ -0,0 +1 @@ +@import 'tailwindcss'; diff --git a/packages/@tailwindcss-cli/src/commands/canonicalize/index.ts b/packages/@tailwindcss-cli/src/commands/canonicalize/index.ts new file mode 100644 index 000000000..5aecfe51e --- /dev/null +++ b/packages/@tailwindcss-cli/src/commands/canonicalize/index.ts @@ -0,0 +1,362 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import fs from 'node:fs/promises' +import path from 'node:path' +import { createInterface } from 'node:readline' +import type { Readable, Writable } from 'node:stream' +import { compare } from '../../../../tailwindcss/src/utils/compare' +import { segment } from '../../../../tailwindcss/src/utils/segment' +import { args, type Arg } from '../../utils/args' +import { help } from '../help' + +const css = String.raw +export type OutputFormat = 'text' | 'json' | 'jsonl' + +export interface CandidateGroupResult { + input: string + output: string + changed: boolean +} + +export interface RunCommandLineOptions { + argv?: string[] + cwd?: string + stdin?: string | null + stdinIsTTY?: boolean + stdoutIsTTY?: boolean +} + +export interface RunCommandLineResult { + exitCode: number + stdout: string + stderr: string +} + +export function usage() { + return 'tailwindcss canonicalize [classes...]' +} + +function usageWithCss() { + return 'tailwindcss canonicalize --css input.css [classes...]' +} + +function usageWithStream() { + return 'tailwindcss canonicalize --stream [--css input.css]' +} + +export function options() { + return { + '--css': { + type: 'string', + description: + 'CSS entry file used to load the Tailwind design system (defaults to `@import "tailwindcss";`)', + }, + '--format': { + type: 'string', + description: 'Output format', + default: 'text', + values: ['text', 'json', 'jsonl'], + }, + '--stream': { + type: 'boolean', + description: 'Read candidate groups from stdin line by line and write results to stdout', + default: false, + }, + } satisfies Arg +} + +const sharedOptions = { + '--help': { + type: 'boolean', + description: 'Display usage information', + alias: '-h', + default: false, + }, +} satisfies Arg + +export async function runCommandLine({ + argv = process.argv.slice(2), + cwd = process.cwd(), + stdin = null, + stdinIsTTY = process.stdin.isTTY, + stdoutIsTTY = process.stdout.isTTY, +}: RunCommandLineOptions = {}): Promise { + try { + let flags = args( + { + ...options(), + ...sharedOptions, + }, + argv, + ) + + let format = parseFormat(flags['--format'] ?? 'text') + + if ((stdoutIsTTY && argv.length === 0) || flags['--help']) { + return { + exitCode: 0, + stdout: helpMessage() ?? '', + stderr: '', + } + } + + if (flags['--stream']) { + await streamStdin({ + css: flags['--css'], + cwd, + format, + input: process.stdin, + output: process.stdout, + }) + return { exitCode: 0, stdout: '', stderr: '' } + } + + let inputs = flags._.length > 0 ? flags._ : await readCandidateGroups({ stdin, stdinIsTTY }) + + if (inputs.length === 0) { + return usageError('No candidate groups provided') + } + + let results = await processCandidateGroups({ + css: flags['--css'], + cwd, + inputs, + }) + + return { + exitCode: 0, + stdout: formatCandidateResults(results, format), + stderr: '', + } + } catch (error) { + return { + exitCode: 1, + stdout: '', + stderr: error instanceof Error ? error.message : String(error), + } + } +} + +export async function streamStdin({ + css: cssFile, + cwd, + format, + input, + output, +}: { + css: string | null + cwd: string + format: OutputFormat + input: Readable + output: Writable +}): Promise { + let designSystem = await loadDesignSystem(cssFile, cwd) + let rl = createInterface({ input }) + let first = true + + if (format === 'json') { + output.write('[') + } + + for await (let line of rl) { + let result = createCandidateGroupResult(designSystem, line) + + switch (format) { + case 'text': { + output.write(result.output + '\n') + break + } + + case 'jsonl': { + output.write(JSON.stringify(result) + '\n') + break + } + + case 'json': { + if (first) { + output.write('\n') + } else { + output.write(',\n') + } + + output.write(indent(JSON.stringify(result, null, 2), 2)) + first = false + break + } + } + } + + if (format === 'json') { + output.write(first ? ']' : '\n]') + } +} + +export function readCandidateGroups({ + stdin, + stdinIsTTY, +}: { + stdin: string | null + stdinIsTTY: boolean +}) { + if (stdin !== null) { + return Promise.resolve(splitCandidateGroups(stdin)) + } + + if (stdinIsTTY) { + return Promise.resolve([]) + } + + return drainStdin().then(splitCandidateGroups) +} + +export async 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 processCandidateGroups({ + css, + cwd = process.cwd(), + inputs, +}: { + css: string | null + cwd?: string + inputs: string[] +}): Promise { + let designSystem = await loadDesignSystem(css, cwd) + + return inputs.map((input) => createCandidateGroupResult(designSystem, input)) +} + +export function formatCandidateResults(results: CandidateGroupResult[], format: OutputFormat) { + switch (format) { + case 'json': + return JSON.stringify(results, null, 2) + case 'jsonl': + return results.map((result) => JSON.stringify(result)).join('\n') + case 'text': + return results.map((result) => result.output).join('\n') + } +} + +function helpMessage() { + return help({ + render: false, + usage: [usage(), usageWithCss(), usageWithStream()], + options: { + ...options(), + ...sharedOptions, + }, + }) +} + +async function loadDesignSystem(cssFile: string | null, cwd: string) { + if (cssFile === null) { + return __unstable__loadDesignSystem( + css` + @import 'tailwindcss'; + `, + { base: cwd }, + ) + } + + let resolvedCssFile = path.resolve(cwd, cssFile) + let content = await fs.readFile(resolvedCssFile, 'utf8') + return __unstable__loadDesignSystem(content, { + base: path.dirname(resolvedCssFile), + }) +} + +function splitCandidateGroups(input: string) { + return input + .split(/\r?\n/g) + .map((line) => line.trim()) + .filter((line) => line.length > 0) +} + +function parseFormat(input: string): OutputFormat { + if (input === 'text' || input === 'json' || input === 'jsonl') { + return input + } + + throw new Error(`Invalid value for --format: ${input}`) +} + +function usageError(message: string): RunCommandLineResult { + return { + exitCode: 1, + stdout: helpMessage() ?? '', + stderr: message, + } +} + +function splitCandidates(input: string) { + let trimmedInput = input.trim() + if (trimmedInput.length === 0) return [] + + return segment(trimmedInput, ' ') + .map((candidate) => candidate.trim()) + .filter((candidate) => candidate.length > 0) +} + +function canonicalize( + designSystem: Awaited>, + input: string, +) { + let candidates = splitCandidates(input) + candidates = designSystem.canonicalizeCandidates(candidates, { + collapse: true, + logicalToPhysical: true, + }) + return defaultSort(designSystem.getClassOrder(candidates)).join(' ') +} + +function createCandidateGroupResult( + designSystem: Awaited>, + input: string, +): CandidateGroupResult { + let output = canonicalize(designSystem, input) + + return { + input, + output, + changed: output !== input, + } +} + +function indent(input: string, size: number) { + let prefix = ' '.repeat(size) + return input + .split('\n') + .map((line) => prefix + line) + .join('\n') +} + +function defaultSort(entries: [string, bigint | null][]) { + return entries + .slice() + .sort(([candidateA, orderA], [candidateZ, orderZ]) => { + if (orderA === orderZ) return compare(candidateA, candidateZ) + if (orderA === null) return -1 + if (orderZ === null) return 1 + return bigSign(orderA - orderZ) || compare(candidateA, candidateZ) + }) + .map(([candidate]) => candidate) +} + +function bigSign(value: bigint) { + if (value > 0n) { + return 1 + } else if (value === 0n) { + return 0 + } else { + return -1 + } +} diff --git a/packages/@tailwindcss-cli/src/commands/help/index.ts b/packages/@tailwindcss-cli/src/commands/help/index.ts index 20ee78208..b557d03e0 100644 --- a/packages/@tailwindcss-cli/src/commands/help/index.ts +++ b/packages/@tailwindcss-cli/src/commands/help/index.ts @@ -3,30 +3,34 @@ import type { Arg } from '../../utils/args' import { UI, header, highlight, indent, println, wordWrap } from '../../utils/renderer' export function help({ + render = true, invalid, usage, options, }: { + render?: boolean invalid?: string usage?: string[] options?: Arg }) { // Available terminal width - let width = process.stdout.columns + let width = process.stdout.columns ?? 80 + let lines: string[] = [] + let writeLine = render ? println : (value = '') => void lines.push(value) // Render header - println(header()) + writeLine(header()) // Render the invalid command if (invalid) { - println() - println(`${pc.dim('Invalid command:')} ${invalid}`) + writeLine() + writeLine(`${pc.dim('Invalid command:')} ${invalid}`) } // Render usage if (usage && usage.length > 0) { - println() - println(pc.dim('Usage:')) + writeLine() + writeLine(pc.dim('Usage:')) for (let [idx, example] of usage.entries()) { // Split the usage example into the command and its options. This allows // us to wrap the options based on the available width of the terminal. @@ -65,7 +69,7 @@ export function help({ // tailwindcss other [--watch] [options...] // ``` if (lines.length > 1 && idx !== 0) { - println() + writeLine() } // Print the usage examples based on available width of the terminal. @@ -86,9 +90,9 @@ export function help({ // ``` // // > Note how the second line is indented to align with the first line. - println(indent(`${command}${lines.shift()}`)) + writeLine(indent(`${command}${lines.shift()}`)) for (let line of lines) { - println(indent(line, command.length)) + writeLine(indent(line, command.length)) } } } @@ -112,7 +116,11 @@ export function help({ // `alias` followed by `, ` and followed by the `flag`. let maxOptionLength = 0 - for (let [flag, { alias }] of Object.entries(options)) { + for (let [flag, { alias, values }] of Object.entries(options)) { + if (values?.length) { + flag += `[=${values.join(', ')}]` + } + // The option string, which is the combination of the alias and the flag // but already properly indented based on the other aliases to ensure // everything is aligned properly. @@ -127,8 +135,8 @@ export function help({ maxOptionLength = Math.max(maxOptionLength, option.length) } - println() - println(pc.dim('Options:')) + writeLine() + writeLine(pc.dim('Options:')) // The minimum amount of dots between the option and the description. let minimumGap = 8 @@ -156,15 +164,19 @@ export function help({ ) // Print the option, the spacer dots and the start of the description. - println( + writeLine( indent(`${pc.blue(option)} ${pc.dim(pc.gray('\u00B7')).repeat(dotCount)} ${lines.shift()}`), ) // Print the remaining lines of the description, indenting them to align // with the start of the description. for (let line of lines) { - println(indent(`${' '.repeat(option.length + dotCount + spaces)}${line}`)) + writeLine(indent(`${' '.repeat(option.length + dotCount + spaces)}${line}`)) } } } + + if (!render) { + return lines.join('\n') + } } diff --git a/packages/@tailwindcss-cli/src/index.ts b/packages/@tailwindcss-cli/src/index.ts index 511781759..1543f7d13 100644 --- a/packages/@tailwindcss-cli/src/index.ts +++ b/packages/@tailwindcss-cli/src/index.ts @@ -3,46 +3,80 @@ import { args, type Arg } from './utils/args' import * as build from './commands/build' +import * as canonicalize from './commands/canonicalize' import { help } from './commands/help' const sharedOptions = { '--help': { type: 'boolean', description: 'Display usage information', alias: '-h' }, } satisfies Arg -const flags = args({ - ...build.options(), - ...sharedOptions, -}) -const command = flags._[0] - -// Right now we don't support any sub-commands. Let's show the help message -// instead. -if (command) { - help({ - invalid: command, - usage: ['tailwindcss [options]'], - options: { ...build.options(), ...sharedOptions }, - }) - process.exit(1) +function buildUsage(command = 'tailwindcss') { + return `${command} [--input input.css] [--output output.css] [--watch] [--poll=ms] [options…]` } -// Display main help message if no command is being used. -// -// E.g.: -// -// - `tailwindcss` // should show the help message -// -// E.g.: implicit `build` command -// -// - `tailwindcss -o output.css` // should run the build command, not show the help message -// - `tailwindcss > output.css` // should run the build command, not show the help message -if ((process.stdout.isTTY && !flags['--output']) || flags['--help']) { +function rootHelp({ invalid }: { invalid?: string } = {}) { help({ - usage: ['tailwindcss [--input input.css] [--output output.css] [--watch] [options…]'], + invalid, + usage: [buildUsage('tailwindcss'), buildUsage('tailwindcss build'), canonicalize.usage()], + commands: { + build: 'Build your CSS', + canonicalize: 'Canonicalize candidate lists', + }, options: { ...build.options(), ...sharedOptions }, }) - process.exit(0) } -// Handle the build command -build.handle(flags) +async function run() { + let argv = process.argv.slice(2) + let command = argv[0] + let rootFlags = args({ ...build.options(), ...sharedOptions }, argv) + + if (command === 'build') { + let flags = args({ ...build.options(), ...sharedOptions }, argv.slice(1)) + + if ((process.stdout.isTTY && argv.length === 1) || flags['--help']) { + help({ + usage: [buildUsage('tailwindcss build')], + options: { ...build.options(), ...sharedOptions }, + }) + process.exit(0) + } + + await build.handle(flags) + return + } + + if (command === 'canonicalize') { + let result = await canonicalize.runCommandLine({ argv: argv.slice(1) }) + + if (result.stdout.length > 0) { + process.stdout.write(`${result.stdout}\n`) + } + + if (result.stderr.length > 0) { + process.stderr.write(`${result.stderr}\n`) + } + + process.exitCode = result.exitCode + return + } + + if ((process.stdout.isTTY && command === undefined) || rootFlags['--help']) { + rootHelp() + process.exit(0) + } + + if (command && !command.startsWith('-')) { + rootHelp({ invalid: command }) + process.exit(1) + } + + let flags = args({ + ...build.options(), + ...sharedOptions, + }) + + await build.handle(flags) +} + +await run() diff --git a/packages/@tailwindcss-cli/src/utils/args.test.ts b/packages/@tailwindcss-cli/src/utils/args.test.ts index 51d2c4747..249fb23eb 100644 --- a/packages/@tailwindcss-cli/src/utils/args.test.ts +++ b/packages/@tailwindcss-cli/src/utils/args.test.ts @@ -17,6 +17,38 @@ it('should be possible to parse a single argument', () => { `) }) +it('should only return the last value for duplicate arguments', () => { + expect( + args( + { + '--output': { type: 'string', description: 'Output file' }, + }, + ['--output', 'output.css', '--output', 'override.css'], + ), + ).toMatchInlineSnapshot(` + { + "--output": "override.css", + "_": [], + } + `) +}) + +it('uses last value when flag with "-" is supplied multiple times', () => { + let result = args( + { + '--output': { type: 'string', description: 'Output file', alias: '-o' }, + }, + ['--output', 'output.css', '--output', '-'], + ) + + expect(result).toMatchInlineSnapshot(` + { + "--output": "-", + "_": [], + } + `) +}) + it('should fallback to the default value if no flag is passed', () => { expect( args( @@ -121,3 +153,34 @@ it('should be possible to provide multiple types, and convert the value to that } `) }) + +it('should be possible to provide boolean or number arguments', () => { + let options = { + '--poll': { + type: 'boolean | number', + description: 'Use polling instead of filesystem events when watching', + default: false, + }, + } satisfies Arg + + expect(args(options, ['--poll'])).toMatchInlineSnapshot(` + { + "--poll": true, + "_": [], + } + `) + + expect(args(options, ['--poll=50'])).toMatchInlineSnapshot(` + { + "--poll": 50, + "_": [], + } + `) + + expect(args(options, ['--poll=foo'])).toMatchInlineSnapshot(` + { + "--poll": undefined, + "_": [], + } + `) +}) diff --git a/packages/@tailwindcss-cli/src/utils/args.ts b/packages/@tailwindcss-cli/src/utils/args.ts index 81bd847d5..88b306b05 100644 --- a/packages/@tailwindcss-cli/src/utils/args.ts +++ b/packages/@tailwindcss-cli/src/utils/args.ts @@ -7,6 +7,7 @@ export type Arg = { description: string alias?: `-${string}` default?: Types[keyof Types] + values?: string[] } } @@ -67,8 +68,28 @@ export type Result = { } export function args(options: T, argv = process.argv.slice(2)): Result { + for (let [idx, value] of argv.entries()) { + if (value === '-') { + argv[idx] = '__IO_DEFAULT_VALUE__' + } + } + let parsed = parse(argv) + for (let key in parsed) { + let value = parsed[key] + + if (key !== '_' && Array.isArray(value)) { + value = value[value.length - 1] + } + + if (value === '__IO_DEFAULT_VALUE__') { + value = '-' + } + + parsed[key] = value + } + let result: { _: string[]; [key: string]: unknown } = { _: parsed._, } diff --git a/packages/@tailwindcss-cli/src/utils/renderer.test.ts b/packages/@tailwindcss-cli/src/utils/renderer.test.ts index b9950655f..7acef4551 100644 --- a/packages/@tailwindcss-cli/src/utils/renderer.test.ts +++ b/packages/@tailwindcss-cli/src/utils/renderer.test.ts @@ -1,29 +1,29 @@ import path from 'node:path' import { describe, expect, it } from 'vitest' import { relative, wordWrap } from './renderer' -import { normalizeWindowsSeperators } from './test-helpers' +import { normalizeWindowsSeparators } from './test-helpers' describe('relative', () => { it('should print an absolute path relative to the current working directory', () => { - expect(normalizeWindowsSeperators(relative(path.resolve('index.css')))).toMatchInlineSnapshot( + expect(normalizeWindowsSeparators(relative(path.resolve('index.css')))).toMatchInlineSnapshot( `"./index.css"`, ) }) it('should prefer the shortest value by default', () => { // Shortest between absolute and relative paths - expect(normalizeWindowsSeperators(relative('index.css'))).toMatchInlineSnapshot(`"index.css"`) + expect(normalizeWindowsSeparators(relative('index.css'))).toMatchInlineSnapshot(`"index.css"`) }) it('should be possible to override the current working directory', () => { - expect(normalizeWindowsSeperators(relative('../utils/index.css', '..'))).toMatchInlineSnapshot( + expect(normalizeWindowsSeparators(relative('../utils/index.css', '..'))).toMatchInlineSnapshot( `"./utils/index.css"`, ) }) it('should be possible to always prefer the relative path', () => { expect( - normalizeWindowsSeperators( + normalizeWindowsSeparators( relative('index.css', process.cwd(), { preferAbsoluteIfShorter: false }), ), ).toMatchInlineSnapshot(`"./index.css"`) diff --git a/packages/@tailwindcss-cli/src/utils/renderer.ts b/packages/@tailwindcss-cli/src/utils/renderer.ts index 6b0098586..da817dda7 100644 --- a/packages/@tailwindcss-cli/src/utils/renderer.ts +++ b/packages/@tailwindcss-cli/src/utils/renderer.ts @@ -49,7 +49,9 @@ export function wordWrap(text: string, width: number) { for (let word of words) { let wordLength = stripVTControlCharacters(word).length - if (lineLength + wordLength + 1 > width) { + // A word longer than `width` is kept on its own line rather than + // introducing an empty line before it. + if (lineLength > 0 && lineLength + wordLength + 1 > width) { lines.push(line) line = '' lineLength = 0 diff --git a/packages/@tailwindcss-cli/src/utils/test-helpers.ts b/packages/@tailwindcss-cli/src/utils/test-helpers.ts index 8e61b0a13..30f4fc777 100644 --- a/packages/@tailwindcss-cli/src/utils/test-helpers.ts +++ b/packages/@tailwindcss-cli/src/utils/test-helpers.ts @@ -1,5 +1,5 @@ import path from 'node:path' -export function normalizeWindowsSeperators(p: string) { +export function normalizeWindowsSeparators(p: string) { return path.sep === '\\' ? p.replaceAll('\\', '/') : p } diff --git a/packages/@tailwindcss-node/README.md b/packages/@tailwindcss-node/README.md index 95ec9d87d..5f532607d 100644 --- a/packages/@tailwindcss-node/README.md +++ b/packages/@tailwindcss-node/README.md @@ -13,10 +13,10 @@

- Build Status + Build Status Total Downloads - Latest Release - License + Latest Release + License

--- @@ -27,14 +27,10 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com). ## Community -For help, discussion about best practices, or any other conversation that would benefit from being searchable: +For help, discussion about best practices, or feature ideas: -[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) - -For chatting with others using the framework: - -[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe) +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) ## Contributing -If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. diff --git a/packages/@tailwindcss-node/package.json b/packages/@tailwindcss-node/package.json index 4776236c6..0eced7366 100644 --- a/packages/@tailwindcss-node/package.json +++ b/packages/@tailwindcss-node/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/node", - "version": "4.0.0-beta.7", + "version": "4.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { @@ -19,26 +19,46 @@ ], "publishConfig": { "provenance": true, - "access": "public" + "access": "public", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./require-cache": { + "types": "./dist/require-cache.d.ts", + "default": "./dist/require-cache.js" + }, + "./esm-cache-loader": { + "types": "./dist/esm-cache.loader.d.mts", + "default": "./dist/esm-cache.loader.mjs" + } + } }, "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" + "types": "./src/index.ts", + "import": "./src/index.ts", + "require": "./src/index.cts" }, "./require-cache": { - "types": "./dist/require-cache.d.ts", - "default": "./dist/require-cache.js" + "types": "./src/require-cache.ts", + "import": "./src/require-cache.ts", + "require": "./src/require-cache.cts" }, "./esm-cache-loader": { - "types": "./dist/esm-cache.loader.d.mts", - "default": "./dist/esm-cache.loader.mjs" + "types": "./src/esm-cache.loader.mts", + "default": "./src/esm-cache.loader.mts" } }, "dependencies": { - "enhanced-resolve": "^5.17.1", - "jiti": "^2.4.0", + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "catalog:", + "jiti": "^2.7.0", + "lightningcss": "catalog:", + "magic-string": "^1.1.0", + "source-map-js": "^1.2.1", "tailwindcss": "workspace:*" } } diff --git a/packages/@tailwindcss-node/src/compile.ts b/packages/@tailwindcss-node/src/compile.ts index b946c79de..e5f8782c8 100644 --- a/packages/@tailwindcss-node/src/compile.ts +++ b/packages/@tailwindcss-node/src/compile.ts @@ -2,26 +2,29 @@ import EnhancedResolve from 'enhanced-resolve' import { createJiti, type Jiti } from 'jiti' import fs from 'node:fs' import fsPromises from 'node:fs/promises' -import path, { dirname } from 'node:path' +import path from 'node:path' import { pathToFileURL } from 'node:url' import { __unstable__loadDesignSystem as ___unstable__loadDesignSystem, compile as _compile, compileAst as _compileAst, Features, + Polyfills, } from 'tailwindcss' import type { AstNode } from '../../tailwindcss/src/ast' import { getModuleDependencies } from './get-module-dependencies' import { rewriteUrls } from './urls' -export { Features } +export { Features, Polyfills } export type Resolver = (id: string, base: string) => Promise export interface CompileOptions { base: string + from?: string onDependency: (path: string) => void shouldRewriteUrls?: boolean + polyfills?: Polyfills customCssResolver?: Resolver customJsResolver?: Resolver @@ -29,6 +32,8 @@ export interface CompileOptions { function createCompileOptions({ base, + from, + polyfills, onDependency, shouldRewriteUrls, @@ -37,11 +42,13 @@ function createCompileOptions({ }: CompileOptions) { return { base, + polyfills, + from, async loadModule(id: string, base: string) { return loadModule(id, base, onDependency, customJsResolver) }, - async loadStylesheet(id: string, base: string) { - let sheet = await loadStylesheet(id, base, onDependency, customCssResolver) + async loadStylesheet(id: string, sheetBase: string) { + let sheet = await loadStylesheet(id, sheetBase, onDependency, customCssResolver) if (shouldRewriteUrls) { sheet.content = await rewriteUrls({ @@ -56,10 +63,9 @@ function createCompileOptions({ } } -async function ensureSourceDetectionRootExists( - compiler: { root: Awaited>['root'] }, - base: string, -) { +async function ensureSourceDetectionRootExists(compiler: { + root: Awaited>['root'] +}) { // Verify if the `source(…)` path exists (until the glob pattern starts) if (compiler.root && compiler.root !== 'none') { let globSymbols = /[*{]/ @@ -73,25 +79,27 @@ async function ensureSourceDetectionRootExists( } let exists = await fsPromises - .stat(path.resolve(base, basePath.join('/'))) + .stat(path.resolve(compiler.root.base, basePath.join('/'))) .then((stat) => stat.isDirectory()) .catch(() => false) if (!exists) { - throw new Error(`The \`source(${compiler.root.pattern})\` does not exist`) + throw new Error( + `The \`source(${compiler.root.pattern})\` does not exist or is not a directory.`, + ) } } } export async function compileAst(ast: AstNode[], options: CompileOptions) { let compiler = await _compileAst(ast, createCompileOptions(options)) - await ensureSourceDetectionRootExists(compiler, options.base) + await ensureSourceDetectionRootExists(compiler) return compiler } export async function compile(css: string, options: CompileOptions) { let compiler = await _compile(css, createCompileOptions(options)) - await ensureSourceDetectionRootExists(compiler, options.base) + await ensureSourceDetectionRootExists(compiler) return compiler } @@ -121,7 +129,8 @@ export async function loadModule( let module = await importModule(pathToFileURL(resolvedPath).href) return { - base: dirname(resolvedPath), + path: resolvedPath, + base: path.dirname(resolvedPath), module: module.default ?? module, } } @@ -140,7 +149,8 @@ export async function loadModule( onDependency(file) } return { - base: dirname(resolvedPath), + path: resolvedPath, + base: path.dirname(resolvedPath), module: module.default ?? module, } } @@ -156,18 +166,9 @@ async function loadStylesheet( onDependency(resolvedPath) - if (typeof globalThis.__tw_readFile === 'function') { - let file = await globalThis.__tw_readFile(resolvedPath, 'utf-8') - if (file) { - return { - base: path.dirname(resolvedPath), - content: file, - } - } - } - let file = await fsPromises.readFile(resolvedPath, 'utf-8') return { + path: resolvedPath, base: path.dirname(resolvedPath), content: file, } @@ -193,12 +194,18 @@ async function importModule(path: string): Promise { } } +const modules = [ + 'node_modules', + ...(process.env.NODE_PATH ? [...process.env.NODE_PATH.split(path.delimiter)] : []), +] + const cssResolver = EnhancedResolve.ResolverFactory.createResolver({ fileSystem: new EnhancedResolve.CachedInputFileSystem(fs, 4000), useSyncFileSystemCalls: true, extensions: ['.css'], mainFields: ['style'], conditionNames: ['style'], + modules, }) async function resolveCssId( id: string, @@ -227,6 +234,7 @@ const esmResolver = EnhancedResolve.ResolverFactory.createResolver({ useSyncFileSystemCalls: true, extensions: ['.js', '.json', '.node', '.ts'], conditionNames: ['node', 'import'], + modules, }) const cjsResolver = EnhancedResolve.ResolverFactory.createResolver({ @@ -234,6 +242,7 @@ const cjsResolver = EnhancedResolve.ResolverFactory.createResolver({ useSyncFileSystemCalls: true, extensions: ['.js', '.json', '.node', '.ts'], conditionNames: ['node', 'require'], + modules, }) async function resolveJsId( diff --git a/packages/@tailwindcss-node/src/env.ts b/packages/@tailwindcss-node/src/env.ts index 517ccc6c5..3b1a1b726 100644 --- a/packages/@tailwindcss-node/src/env.ts +++ b/packages/@tailwindcss-node/src/env.ts @@ -1,6 +1,10 @@ export const DEBUG = resolveDebug(process.env.DEBUG) function resolveDebug(debug: typeof process.env.DEBUG) { + if (typeof debug === 'boolean') { + return debug + } + if (debug === undefined) { return false } diff --git a/packages/@tailwindcss-node/src/esm-cache.loader.mts b/packages/@tailwindcss-node/src/esm-cache.loader.mts index edcfc8d77..96d297a29 100644 --- a/packages/@tailwindcss-node/src/esm-cache.loader.mts +++ b/packages/@tailwindcss-node/src/esm-cache.loader.mts @@ -1,22 +1,40 @@ -import { isBuiltin, type ResolveHook } from 'node:module' +import { + isBuiltin, + type ResolveFnOutput, + type ResolveHook, + type ResolveHookContext, + type ResolveHookSync, +} from 'node:module' export let resolve: ResolveHook = async (specifier, context, nextResolve) => { let result = await nextResolve(specifier, context) + return processResolve(context, result) +} - if (result.url === import.meta.url) return result - if (isBuiltin(result.url)) return result - if (!context.parentURL) return result +export let resolveSync: ResolveHookSync = (specifier, context, nextResolve) => { + let result = nextResolve(specifier, context) + return processResolve(context, result) +} - let parent = new URL(context.parentURL) +function processResolve(context: ResolveHookContext, result: ResolveFnOutput) { + try { + if (result.url === import.meta.url) return result + if (isBuiltin(result.url)) return result + if (!context.parentURL) return result - let id = parent.searchParams.get('id') - if (id === null) return result + let parent = new URL(context.parentURL) - let url = new URL(result.url) - url.searchParams.set('id', id) + let id = parent.searchParams.get('id') + if (id === null) return result - return { - ...result, - url: `${url}`, + let url = new URL(result.url) + url.searchParams.set('id', id) + + return { + ...result, + url: `${url}`, + } + } catch { + return result } } diff --git a/packages/@tailwindcss-node/src/index.cts b/packages/@tailwindcss-node/src/index.cts index 2e79e25c7..9f2788933 100644 --- a/packages/@tailwindcss-node/src/index.cts +++ b/packages/@tailwindcss-node/src/index.cts @@ -1,17 +1,26 @@ import * as Module from 'node:module' import { pathToFileURL } from 'node:url' import * as env from './env' +import { resolveSync } from './esm-cache.loader.mjs' export * from './compile' export * from './instrumentation' export * from './normalize-path' +export * from './optimize' +export * from './source-maps' export { env } // In Bun, ESM modules will also populate `require.cache`, so the module hook is // not necessary. if (!process.versions.bun) { // `Module#register` was added in Node v18.19.0 and v20.6.0 + // `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0, + // runtime-deprecating `Module#register` since v26 // // Not calling it means that while ESM dependencies don't get reloaded, the // actual included files will because they cache bust directly via `?id=…` - Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader'))) + if (Module.registerHooks) { + Module.registerHooks({ resolve: resolveSync }) + } else { + Module.register?.(pathToFileURL(require.resolve('@tailwindcss/node/esm-cache-loader'))) + } } diff --git a/packages/@tailwindcss-node/src/index.ts b/packages/@tailwindcss-node/src/index.ts index 4ee2fe805..8eff1466a 100644 --- a/packages/@tailwindcss-node/src/index.ts +++ b/packages/@tailwindcss-node/src/index.ts @@ -1,19 +1,27 @@ import * as Module from 'node:module' import { pathToFileURL } from 'node:url' import * as env from './env' -export { __unstable__loadDesignSystem, compile, compileAst, Features } from './compile' +import { resolveSync } from './esm-cache.loader.mjs' +export * from './compile' export * from './instrumentation' export * from './normalize-path' +export * from './optimize' +export * from './source-maps' export { env } // In Bun, ESM modules will also populate `require.cache`, so the module hook is // not necessary. if (!process.versions.bun) { - let localRequire = Module.createRequire(import.meta.url) - // `Module#register` was added in Node v18.19.0 and v20.6.0 + // `Module#registerHooks` was added in Node v22.15.0 and v23.5.0 and is the preferred API since v25.9.0, + // runtime-deprecating `Module#register` since v26 // // Not calling it means that while ESM dependencies don't get reloaded, the // actual included files will because they cache bust directly via `?id=…` - Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader'))) + if (Module.registerHooks) { + Module.registerHooks({ resolve: resolveSync }) + } else { + let localRequire = Module.createRequire(import.meta.url) + Module.register?.(pathToFileURL(localRequire.resolve('@tailwindcss/node/esm-cache-loader'))) + } } diff --git a/packages/@tailwindcss-node/src/instrumentation.test.ts b/packages/@tailwindcss-node/src/instrumentation.test.ts index 1f06796fd..102c7ed8e 100644 --- a/packages/@tailwindcss-node/src/instrumentation.test.ts +++ b/packages/@tailwindcss-node/src/instrumentation.test.ts @@ -2,7 +2,7 @@ import { stripVTControlCharacters } from 'util' import { expect, it } from 'vitest' import { Instrumentation } from './instrumentation' -it('should add instrumentation', () => { +it('should add instrumentation using start/end markers', () => { let I = new Instrumentation() I.start('Foo') @@ -35,6 +35,83 @@ it('should add instrumentation', () => { }) }) +it('should measure callbacks via the `span` api', () => { + let I = new Instrumentation() + + I.span('Foo', () => { + let x = 1 + for (let i = 0; i < 100; i++) { + I.span('Bar', () => { + x **= 2 + }) + } + }) + + expect.assertions(1) + + I.report((output) => { + expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(` + " + [0.xxms] Foo + [0.xxms] ↳ Bar × 100 + " + `) + }) +}) + +it('should measure async callbacks via the `span` api', async () => { + let I = new Instrumentation() + + await I.span('Foo', async () => { + let x = 1 + for (let i = 0; i < 100; i++) { + I.span('Bar', () => { + x **= 2 + }) + } + await new Promise((r) => setTimeout(r, 500)) + }) + + expect.assertions(2) + + I.report((output) => { + expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(` + " + [0.xxms] Foo + [0.xxms] ↳ Bar × 100 + " + `) + + let [, duration] = stripVTControlCharacters(output).match(/\[(.*)\]/)! + expect(parseFloat(duration)).toBeGreaterThanOrEqual(500 - 2 /* Margin of error */) + }) +}) + +it('should measure blocks until they go out of scope via `using`', () => { + let I = new Instrumentation() + + { + using _ = I.track('Foo') + + let x = 1 + for (let i = 0; i < 100; i++) { + using _ = I.track('Bar') + x **= 2 + } + } + + expect.assertions(1) + + I.report((output) => { + expect(stripVTControlCharacters(output).replace(/\[.*\]/g, '[0.xxms]')).toMatchInlineSnapshot(` + " + [0.xxms] Foo + [0.xxms] ↳ Bar × 100 + " + `) + }) +}) + it('should auto end pending timers when reporting', () => { let I = new Instrumentation() diff --git a/packages/@tailwindcss-node/src/instrumentation.ts b/packages/@tailwindcss-node/src/instrumentation.ts index c408c4db7..e454e6744 100644 --- a/packages/@tailwindcss-node/src/instrumentation.ts +++ b/packages/@tailwindcss-node/src/instrumentation.ts @@ -1,12 +1,21 @@ import { DefaultMap } from '../../tailwindcss/src/utils/default-map' import * as env from './env' +// See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#:~:text=Symbol.dispose,-??=%20Symbol(%22Symbol.dispose +// @ts-expect-error — Ensure Symbol.dispose exists +Symbol.dispose ??= Symbol('Symbol.dispose') +// @ts-expect-error — Ensure Symbol.asyncDispose exists +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose') + export class Instrumentation implements Disposable { #hits = new DefaultMap(() => ({ value: 0 })) #timers = new DefaultMap(() => ({ value: 0n })) #timerStack: { id: string; label: string; namespace: string; value: bigint }[] = [] - constructor(private defaultFlush = (message: string) => process.stderr.write(`${message}\n`)) {} + constructor( + private shouldReport = env.DEBUG, + private defaultFlush = (message: string) => void process.stderr.write(`${message}\n`), + ) {} hit(label: string) { this.#hits.get(label).value++ @@ -40,6 +49,41 @@ export class Instrumentation implements Disposable { this.#timers.get(parent.id).value += elapsed } + track(label: string) { + this.start(label) + let done = false + + return { + [Symbol.dispose]: () => { + if (!done) { + this.end(label) + done = true + } + }, + [Symbol.asyncDispose]: () => { + if (!done) { + this.end(label) + done = true + } + }, + } + } + + span(label: string, fn: () => T): T { + this.start(label) + let isPromise = false + try { + let result = fn() + + isPromise = result && typeof (result as any).then === 'function' + + // @ts-expect-error — TS can't infer that result is a Promise here + return isPromise ? result.finally(() => this.end(label)) : result + } finally { + if (!isPromise) this.end(label) + } + } + reset() { this.#hits.clear() this.#timers.clear() @@ -92,7 +136,7 @@ export class Instrumentation implements Disposable { } [Symbol.dispose]() { - env.DEBUG && this.report() + this.shouldReport && this.report() } } diff --git a/packages/@tailwindcss-node/src/optimize.ts b/packages/@tailwindcss-node/src/optimize.ts new file mode 100644 index 000000000..2f0d39589 --- /dev/null +++ b/packages/@tailwindcss-node/src/optimize.ts @@ -0,0 +1,155 @@ +import remapping from '@jridgewell/remapping' +import { Features, transform } from 'lightningcss' +import MagicString from 'magic-string' + +export interface OptimizeOptions { + /** + * The file being transformed + */ + file?: string + + /** + * Enabled minified output + */ + minify?: boolean + + /** + * The output source map before optimization + * + * If omitted a resulting source map will not be available + */ + map?: string +} + +export interface TransformResult { + code: string + map: string | undefined +} + +export function optimize( + input: string, + { file = 'input.css', minify = false, map }: OptimizeOptions = {}, +): TransformResult { + function optimize(code: Buffer | Uint8Array, map: string | undefined) { + return transform({ + filename: file, + code, + minify, + sourceMap: typeof map !== 'undefined', + inputSourceMap: map, + drafts: { + customMedia: true, + }, + nonStandard: { + deepSelectorCombinator: true, + }, + include: Features.Nesting | Features.MediaQueries, + exclude: Features.LogicalProperties | Features.DirSelector | Features.LightDark, + targets: { + safari: (16 << 16) | (4 << 8), + ios_saf: (16 << 16) | (4 << 8), + firefox: 128 << 16, + chrome: 111 << 16, + }, + errorRecovery: true, + }) + } + + // Running Lightning CSS twice to ensure that adjacent rules are merged after + // nesting is applied. This creates a more optimized output. + let result = optimize(Buffer.from(input), map) + map = result.map?.toString() + + result.warnings = result.warnings.filter((warning) => { + // Ignore warnings about unknown pseudo-classes that are used in frameworks + // such as Vue or Angular and are handled by their own compilers, before + // reaching the browser. + if ( + /'(deep|slotted|global|ng-deep|host-context)' is not recognized as a valid pseudo-/.test( + warning.message, + ) + ) { + return false + } + + return true + }) + + // Because of `errorRecovery: true`, there could be warnings, so let's let the + // user know about them. + if (process.env.NODE_ENV !== 'test' && result.warnings.length > 0) { + let lines = input.split('\n') + + let output = [ + `Found ${result.warnings.length} ${result.warnings.length === 1 ? 'warning' : 'warnings'} while optimizing generated CSS:`, + ] + + for (let [idx, warning] of result.warnings.entries()) { + output.push('') + if (result.warnings.length > 1) { + output.push(`Issue #${idx + 1}:`) + } + + let context = 2 + + let start = Math.max(0, warning.loc.line - context - 1) + let end = Math.min(lines.length, warning.loc.line + context) + + let snippet = lines.slice(start, end).map((line, idx) => { + if (start + idx + 1 === warning.loc.line) { + return `${dim(`\u2502`)} ${line}` + } else { + return dim(`\u2502 ${line}`) + } + }) + + snippet.splice( + warning.loc.line - start, + 0, + `${dim('\u2506')}${' '.repeat(warning.loc.column - 1)} ${yellow(`${dim('^--')} ${warning.message}`)}`, + `${dim('\u2506')}`, + ) + + output.push(...snippet) + } + output.push('') + + console.warn(output.join('\n')) + } + + result = optimize(result.code, map) + map = result.map?.toString() + + let code = result.code.toString() + + // Work around an issue where the media query range syntax transpilation + // generates code that is invalid with `@media` queries level 3. + let magic = new MagicString(code) + magic.replaceAll('@media not (', '@media not all and (') + + // We have to use a source-map-preserving method of replacing the content + // which requires the use of Magic String + remapping(…) to make sure + // the resulting map is correct + if (map !== undefined && magic.hasChanged()) { + let magicMap = magic.generateMap({ source: 'original', hires: 'boundary' }).toString() + + let remapped = remapping([magicMap, map], () => null) + + map = remapped.toString() + } + + code = magic.toString() + + return { + code, + map, + } +} + +function dim(str: string) { + return `\x1B[2m${str}\x1B[22m` +} + +function yellow(str: string) { + return `\x1B[33m${str}\x1B[39m` +} diff --git a/packages/@tailwindcss-node/src/require-cache.ts b/packages/@tailwindcss-node/src/require-cache.ts new file mode 100644 index 000000000..ea0562019 --- /dev/null +++ b/packages/@tailwindcss-node/src/require-cache.ts @@ -0,0 +1,5 @@ +export function clearRequireCache(files: string[]) { + for (let key of files) { + delete require.cache[key] + } +} diff --git a/packages/@tailwindcss-node/src/source-maps.test.ts b/packages/@tailwindcss-node/src/source-maps.test.ts new file mode 100644 index 000000000..96ff8f05c --- /dev/null +++ b/packages/@tailwindcss-node/src/source-maps.test.ts @@ -0,0 +1,8 @@ +import { expect, it } from 'vitest' +import { toSourceMap } from './source-maps' + +it('should emit source maps', () => { + let map = toSourceMap('{"version":3,"sources":[],"names":[],"mappings":""}') + + expect(map.comment('app.css.map')).toBe('/*# sourceMappingURL=app.css.map */\n') +}) diff --git a/packages/@tailwindcss-node/src/source-maps.ts b/packages/@tailwindcss-node/src/source-maps.ts new file mode 100644 index 000000000..58b79cb25 --- /dev/null +++ b/packages/@tailwindcss-node/src/source-maps.ts @@ -0,0 +1,60 @@ +import { SourceMapGenerator } from 'source-map-js' +import type { DecodedSource, DecodedSourceMap } from '../../tailwindcss/src/source-maps/source-map' +import { DefaultMap } from '../../tailwindcss/src/utils/default-map' + +export type { DecodedSource, DecodedSourceMap } +export interface SourceMap { + readonly raw: string + readonly inline: string + comment(url: string): string +} + +function serializeSourceMap(map: DecodedSourceMap): string { + let generator = new SourceMapGenerator() + + let id = 1 + let sourceTable = new DefaultMap< + DecodedSource | null, + { + url: string + content: string + } + >((src) => { + return { + url: src?.url ?? ``, + content: src?.content ?? '', + } + }) + + for (let mapping of map.mappings) { + let original = sourceTable.get(mapping.originalPosition?.source ?? null) + + generator.addMapping({ + generated: mapping.generatedPosition, + original: mapping.originalPosition, + source: original.url, + name: mapping.name, + }) + + generator.setSourceContent(original.url, original.content) + } + + return generator.toString() +} + +export function toSourceMap(map: DecodedSourceMap | string): SourceMap { + let raw = typeof map === 'string' ? map : serializeSourceMap(map) + + function comment(url: string) { + return `/*# sourceMappingURL\x3d${url} */\n` + } + + return { + raw, + get inline() { + let inlined = Buffer.from(raw, 'utf-8').toString('base64') + return comment(`data:application/json;base64,${inlined}`) + }, + comment, + } +} diff --git a/packages/@tailwindcss-node/src/urls.test.ts b/packages/@tailwindcss-node/src/urls.test.ts index 3378e45ed..16ba352a7 100644 --- a/packages/@tailwindcss-node/src/urls.test.ts +++ b/packages/@tailwindcss-node/src/urls.test.ts @@ -24,6 +24,20 @@ test('URLs can be rewritten', async () => { background: url('/image.jpg'); background: url("/image.jpg"); + /* Potentially Vite-aliased URLs: ignored */ + background: url(~/image.jpg); + background: url(~/foo/image.jpg); + background: url('~/image.jpg'); + background: url("~/image.jpg"); + background: url(#/image.jpg); + background: url(#/foo/image.jpg); + background: url('#/image.jpg'); + background: url("#/image.jpg"); + background: url(@/image.jpg); + background: url(@/foo/image.jpg); + background: url('@/image.jpg'); + background: url("@/image.jpg"); + /* External URL: ignored */ background: url(http://example.com/image.jpg); background: url('http://example.com/image.jpg'); @@ -109,6 +123,18 @@ test('URLs can be rewritten', async () => { background: url(/foo/image.jpg); background: url('/image.jpg'); background: url("/image.jpg"); + background: url(~/image.jpg); + background: url(~/foo/image.jpg); + background: url('~/image.jpg'); + background: url("~/image.jpg"); + background: url(#/image.jpg); + background: url(#/foo/image.jpg); + background: url('#/image.jpg'); + background: url("#/image.jpg"); + background: url(@/image.jpg); + background: url(@/foo/image.jpg); + background: url('@/image.jpg'); + background: url("@/image.jpg"); background: url(http://example.com/image.jpg); background: url('http://example.com/image.jpg'); background: url("http://example.com/image.jpg"); diff --git a/packages/@tailwindcss-node/src/urls.ts b/packages/@tailwindcss-node/src/urls.ts index c4d56deb7..61695a9d1 100644 --- a/packages/@tailwindcss-node/src/urls.ts +++ b/packages/@tailwindcss-node/src/urls.ts @@ -5,8 +5,9 @@ // Minor modifications have been made to work with the Tailwind CSS codebase import * as path from 'node:path' -import { toCss, walk } from '../../tailwindcss/src/ast' +import { toCss } from '../../tailwindcss/src/ast' import { parse } from '../../tailwindcss/src/css-parser' +import { walk } from '../../tailwindcss/src/walk' import { normalizePath } from './normalize-path' const cssUrlRE = @@ -149,9 +150,12 @@ async function doUrlReplace( return `${funcName}(${wrap}${newUrl}${wrap})` } -function skipUrlReplacer(rawUrl: string) { +function skipUrlReplacer(rawUrl: string, aliases?: string[]) { return ( - isExternalUrl(rawUrl) || isDataUrl(rawUrl) || rawUrl[0] === '#' || functionCallRE.test(rawUrl) + isExternalUrl(rawUrl) || + isDataUrl(rawUrl) || + !rawUrl[0].match(/[.a-zA-Z0-9_]/) || + functionCallRE.test(rawUrl) ) } diff --git a/packages/@tailwindcss-node/tsconfig.json b/packages/@tailwindcss-node/tsconfig.json index 6ae022f65..d2131cbeb 100644 --- a/packages/@tailwindcss-node/tsconfig.json +++ b/packages/@tailwindcss-node/tsconfig.json @@ -1,3 +1,6 @@ { "extends": "../tsconfig.base.json", + "compilerOptions": { + "verbatimModuleSyntax": false, + }, } diff --git a/packages/@tailwindcss-node/tsup.config.ts b/packages/@tailwindcss-node/tsup.config.ts index 0f2ac5855..3f2d184d5 100644 --- a/packages/@tailwindcss-node/tsup.config.ts +++ b/packages/@tailwindcss-node/tsup.config.ts @@ -6,23 +6,35 @@ export default defineConfig([ minify: true, dts: true, entry: ['src/index.cts'], + define: { + 'process.env.NODE_ENV': '"production"', + }, }, { format: ['esm'], minify: true, dts: true, entry: ['src/index.ts'], + define: { + 'process.env.NODE_ENV': '"production"', + }, }, { format: ['esm'], minify: true, dts: true, entry: ['src/esm-cache.loader.mts'], + define: { + 'process.env.NODE_ENV': '"production"', + }, }, { format: ['cjs'], minify: true, dts: true, entry: ['src/require-cache.cts'], + define: { + 'process.env.NODE_ENV': '"production"', + }, }, ]) diff --git a/packages/@tailwindcss-postcss/README.md b/packages/@tailwindcss-postcss/README.md index 95ec9d87d..3facf2887 100644 --- a/packages/@tailwindcss-postcss/README.md +++ b/packages/@tailwindcss-postcss/README.md @@ -13,10 +13,10 @@

- Build Status + Build Status Total Downloads - Latest Release - License + Latest Release + License

--- @@ -27,14 +27,85 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com). ## Community -For help, discussion about best practices, or any other conversation that would benefit from being searchable: +For help, discussion about best practices, or feature ideas: -[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) - -For chatting with others using the framework: - -[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe) +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) ## Contributing -If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. + +--- + +## `@tailwindcss/postcss` plugin API + +### Changing where the plugin searches for source files + +You can use the `base` option (defaults to the current working directory) to change the directory in which the plugin searches for source files: + +```js +import tailwindcss from '@tailwindcss/postcss' + +export default { + plugins: [ + tailwindcss({ + base: path.resolve(__dirname, './path'), + }), + ], +} +``` + +### Enabling or disabling Lightning CSS + +By default, this plugin detects whether or not the CSS is being built for production by checking the `NODE_ENV` environment variable. When building for production Lightning CSS will be enabled otherwise it is disabled. + +If you want to always enable or disable Lightning CSS the `optimize` option may be used: + +```js +import tailwindcss from '@tailwindcss/postcss' + +export default { + plugins: [ + tailwindcss({ + // Enable or disable Lightning CSS + optimize: false, + }), + ], +} +``` + +It's also possible to keep Lightning CSS enabled but disable minification: + +```js +import tailwindcss from '@tailwindcss/postcss' + +export default { + plugins: [ + tailwindcss({ + optimize: { minify: false }, + }), + ], +} +``` + +### Enabling or disabling `url(…)` rewriting + +Our PostCSS plugin can rewrite `url(…)`s for you since it also handles `@import` (no `postcss-import` is needed). This feature is enabled by default. + +In some situations the bundler or framework you're using may provide this feature itself. In this case you can set `transformAssetUrls` to `false` to disable this feature: + +```js +import tailwindcss from '@tailwindcss/postcss' + +export default { + plugins: [ + tailwindcss({ + // Disable `url(…)` rewriting + transformAssetUrls: false, + + // Enable `url(…)` rewriting (the default) + transformAssetUrls: true, + }), + ], +} +``` diff --git a/packages/@tailwindcss-postcss/package.json b/packages/@tailwindcss-postcss/package.json index 7e567a8d0..891578509 100644 --- a/packages/@tailwindcss-postcss/package.json +++ b/packages/@tailwindcss-postcss/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/postcss", - "version": "4.0.0-beta.7", + "version": "4.3.3", "description": "PostCSS plugin for Tailwind CSS, a utility-first CSS framework for rapidly building custom user interfaces", "license": "MIT", "repository": { @@ -24,24 +24,28 @@ }, "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } } }, "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "workspace:^", - "@tailwindcss/oxide": "workspace:^", - "lightningcss": "catalog:", - "postcss": "^8.4.41", + "@tailwindcss/node": "workspace:*", + "@tailwindcss/oxide": "workspace:*", + "postcss": "catalog:", "tailwindcss": "workspace:*" }, "devDependencies": { "@types/node": "catalog:", "@types/postcss-import": "14.0.3", - "dedent": "1.5.3", + "dedent": "catalog:", "internal-example-plugin": "workspace:*", - "postcss-import": "^16.1.0" + "postcss-import": "^16.1.1" } } diff --git a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap index cf0794ff2..05087fa39 100644 --- a/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap +++ b/packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap @@ -1,373 +1,27 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` -"@layer theme { - :root { - --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --color-red-50: oklch(.971 .013 17.38); - --color-red-100: oklch(.936 .032 17.717); - --color-red-200: oklch(.885 .062 18.334); - --color-red-300: oklch(.808 .114 19.571); - --color-red-400: oklch(.704 .191 22.216); - --color-red-500: oklch(.637 .237 25.331); - --color-red-600: oklch(.577 .245 27.325); - --color-red-700: oklch(.505 .213 27.518); - --color-red-800: oklch(.444 .177 26.899); - --color-red-900: oklch(.396 .141 25.723); - --color-red-950: oklch(.258 .092 26.042); - --color-orange-50: oklch(.98 .016 73.684); - --color-orange-100: oklch(.954 .038 75.164); - --color-orange-200: oklch(.901 .076 70.697); - --color-orange-300: oklch(.837 .128 66.29); - --color-orange-400: oklch(.75 .183 55.934); - --color-orange-500: oklch(.705 .213 47.604); - --color-orange-600: oklch(.646 .222 41.116); - --color-orange-700: oklch(.553 .195 38.402); - --color-orange-800: oklch(.47 .157 37.304); - --color-orange-900: oklch(.408 .123 38.172); - --color-orange-950: oklch(.266 .079 36.259); - --color-amber-50: oklch(.987 .022 95.277); - --color-amber-100: oklch(.962 .059 95.617); - --color-amber-200: oklch(.924 .12 95.746); - --color-amber-300: oklch(.879 .169 91.605); - --color-amber-400: oklch(.828 .189 84.429); - --color-amber-500: oklch(.769 .188 70.08); - --color-amber-600: oklch(.666 .179 58.318); - --color-amber-700: oklch(.555 .163 48.998); - --color-amber-800: oklch(.473 .137 46.201); - --color-amber-900: oklch(.414 .112 45.904); - --color-amber-950: oklch(.279 .077 45.635); - --color-yellow-50: oklch(.987 .026 102.212); - --color-yellow-100: oklch(.973 .071 103.193); - --color-yellow-200: oklch(.945 .129 101.54); - --color-yellow-300: oklch(.905 .182 98.111); - --color-yellow-400: oklch(.852 .199 91.936); - --color-yellow-500: oklch(.795 .184 86.047); - --color-yellow-600: oklch(.681 .162 75.834); - --color-yellow-700: oklch(.554 .135 66.442); - --color-yellow-800: oklch(.476 .114 61.907); - --color-yellow-900: oklch(.421 .095 57.708); - --color-yellow-950: oklch(.286 .066 53.813); - --color-lime-50: oklch(.986 .031 120.757); - --color-lime-100: oklch(.967 .067 122.328); - --color-lime-200: oklch(.938 .127 124.321); - --color-lime-300: oklch(.897 .196 126.665); - --color-lime-400: oklch(.841 .238 128.85); - --color-lime-500: oklch(.768 .233 130.85); - --color-lime-600: oklch(.648 .2 131.684); - --color-lime-700: oklch(.532 .157 131.589); - --color-lime-800: oklch(.453 .124 130.933); - --color-lime-900: oklch(.405 .101 131.063); - --color-lime-950: oklch(.274 .072 132.109); - --color-green-50: oklch(.982 .018 155.826); - --color-green-100: oklch(.962 .044 156.743); - --color-green-200: oklch(.925 .084 155.995); - --color-green-300: oklch(.871 .15 154.449); - --color-green-400: oklch(.792 .209 151.711); - --color-green-500: oklch(.723 .219 149.579); - --color-green-600: oklch(.627 .194 149.214); - --color-green-700: oklch(.527 .154 150.069); - --color-green-800: oklch(.448 .119 151.328); - --color-green-900: oklch(.393 .095 152.535); - --color-green-950: oklch(.266 .065 152.934); - --color-emerald-50: oklch(.979 .021 166.113); - --color-emerald-100: oklch(.95 .052 163.051); - --color-emerald-200: oklch(.905 .093 164.15); - --color-emerald-300: oklch(.845 .143 164.978); - --color-emerald-400: oklch(.765 .177 163.223); - --color-emerald-500: oklch(.696 .17 162.48); - --color-emerald-600: oklch(.596 .145 163.225); - --color-emerald-700: oklch(.508 .118 165.612); - --color-emerald-800: oklch(.432 .095 166.913); - --color-emerald-900: oklch(.378 .077 168.94); - --color-emerald-950: oklch(.262 .051 172.552); - --color-teal-50: oklch(.984 .014 180.72); - --color-teal-100: oklch(.953 .051 180.801); - --color-teal-200: oklch(.91 .096 180.426); - --color-teal-300: oklch(.855 .138 181.071); - --color-teal-400: oklch(.777 .152 181.912); - --color-teal-500: oklch(.704 .14 182.503); - --color-teal-600: oklch(.6 .118 184.704); - --color-teal-700: oklch(.511 .096 186.391); - --color-teal-800: oklch(.437 .078 188.216); - --color-teal-900: oklch(.386 .063 188.416); - --color-teal-950: oklch(.277 .046 192.524); - --color-cyan-50: oklch(.984 .019 200.873); - --color-cyan-100: oklch(.956 .045 203.388); - --color-cyan-200: oklch(.917 .08 205.041); - --color-cyan-300: oklch(.865 .127 207.078); - --color-cyan-400: oklch(.789 .154 211.53); - --color-cyan-500: oklch(.715 .143 215.221); - --color-cyan-600: oklch(.609 .126 221.723); - --color-cyan-700: oklch(.52 .105 223.128); - --color-cyan-800: oklch(.45 .085 224.283); - --color-cyan-900: oklch(.398 .07 227.392); - --color-cyan-950: oklch(.302 .056 229.695); - --color-sky-50: oklch(.977 .013 236.62); - --color-sky-100: oklch(.951 .026 236.824); - --color-sky-200: oklch(.901 .058 230.902); - --color-sky-300: oklch(.828 .111 230.318); - --color-sky-400: oklch(.746 .16 232.661); - --color-sky-500: oklch(.685 .169 237.323); - --color-sky-600: oklch(.588 .158 241.966); - --color-sky-700: oklch(.5 .134 242.749); - --color-sky-800: oklch(.443 .11 240.79); - --color-sky-900: oklch(.391 .09 240.876); - --color-sky-950: oklch(.293 .066 243.157); - --color-blue-50: oklch(.97 .014 254.604); - --color-blue-100: oklch(.932 .032 255.585); - --color-blue-200: oklch(.882 .059 254.128); - --color-blue-300: oklch(.809 .105 251.813); - --color-blue-400: oklch(.707 .165 254.624); - --color-blue-500: oklch(.623 .214 259.815); - --color-blue-600: oklch(.546 .245 262.881); - --color-blue-700: oklch(.488 .243 264.376); - --color-blue-800: oklch(.424 .199 265.638); - --color-blue-900: oklch(.379 .146 265.522); - --color-blue-950: oklch(.282 .091 267.935); - --color-indigo-50: oklch(.962 .018 272.314); - --color-indigo-100: oklch(.93 .034 272.788); - --color-indigo-200: oklch(.87 .065 274.039); - --color-indigo-300: oklch(.785 .115 274.713); - --color-indigo-400: oklch(.673 .182 276.935); - --color-indigo-500: oklch(.585 .233 277.117); - --color-indigo-600: oklch(.511 .262 276.966); - --color-indigo-700: oklch(.457 .24 277.023); - --color-indigo-800: oklch(.398 .195 277.366); - --color-indigo-900: oklch(.359 .144 278.697); - --color-indigo-950: oklch(.257 .09 281.288); - --color-violet-50: oklch(.969 .016 293.756); - --color-violet-100: oklch(.943 .029 294.588); - --color-violet-200: oklch(.894 .057 293.283); - --color-violet-300: oklch(.811 .111 293.571); - --color-violet-400: oklch(.702 .183 293.541); - --color-violet-500: oklch(.606 .25 292.717); - --color-violet-600: oklch(.541 .281 293.009); - --color-violet-700: oklch(.491 .27 292.581); - --color-violet-800: oklch(.432 .232 292.759); - --color-violet-900: oklch(.38 .189 293.745); - --color-violet-950: oklch(.283 .141 291.089); - --color-purple-50: oklch(.977 .014 308.299); - --color-purple-100: oklch(.946 .033 307.174); - --color-purple-200: oklch(.902 .063 306.703); - --color-purple-300: oklch(.827 .119 306.383); - --color-purple-400: oklch(.714 .203 305.504); - --color-purple-500: oklch(.627 .265 303.9); - --color-purple-600: oklch(.558 .288 302.321); - --color-purple-700: oklch(.496 .265 301.924); - --color-purple-800: oklch(.438 .218 303.724); - --color-purple-900: oklch(.381 .176 304.987); - --color-purple-950: oklch(.291 .149 302.717); - --color-fuchsia-50: oklch(.977 .017 320.058); - --color-fuchsia-100: oklch(.952 .037 318.852); - --color-fuchsia-200: oklch(.903 .076 319.62); - --color-fuchsia-300: oklch(.833 .145 321.434); - --color-fuchsia-400: oklch(.74 .238 322.16); - --color-fuchsia-500: oklch(.667 .295 322.15); - --color-fuchsia-600: oklch(.591 .293 322.896); - --color-fuchsia-700: oklch(.518 .253 323.949); - --color-fuchsia-800: oklch(.452 .211 324.591); - --color-fuchsia-900: oklch(.401 .17 325.612); - --color-fuchsia-950: oklch(.293 .136 325.661); - --color-pink-50: oklch(.971 .014 343.198); - --color-pink-100: oklch(.948 .028 342.258); - --color-pink-200: oklch(.899 .061 343.231); - --color-pink-300: oklch(.823 .12 346.018); - --color-pink-400: oklch(.718 .202 349.761); - --color-pink-500: oklch(.656 .241 354.308); - --color-pink-600: oklch(.592 .249 .584); - --color-pink-700: oklch(.525 .223 3.958); - --color-pink-800: oklch(.459 .187 3.815); - --color-pink-900: oklch(.408 .153 2.432); - --color-pink-950: oklch(.284 .109 3.907); - --color-rose-50: oklch(.969 .015 12.422); - --color-rose-100: oklch(.941 .03 12.58); - --color-rose-200: oklch(.892 .058 10.001); - --color-rose-300: oklch(.81 .117 11.638); - --color-rose-400: oklch(.712 .194 13.428); - --color-rose-500: oklch(.645 .246 16.439); - --color-rose-600: oklch(.586 .253 17.585); - --color-rose-700: oklch(.514 .222 16.935); - --color-rose-800: oklch(.455 .188 13.697); - --color-rose-900: oklch(.41 .159 10.272); - --color-rose-950: oklch(.271 .105 12.094); - --color-slate-50: oklch(.984 .003 247.858); - --color-slate-100: oklch(.968 .007 247.896); - --color-slate-200: oklch(.929 .013 255.508); - --color-slate-300: oklch(.869 .022 252.894); - --color-slate-400: oklch(.704 .04 256.788); - --color-slate-500: oklch(.554 .046 257.417); - --color-slate-600: oklch(.446 .043 257.281); - --color-slate-700: oklch(.372 .044 257.287); - --color-slate-800: oklch(.279 .041 260.031); - --color-slate-900: oklch(.208 .042 265.755); - --color-slate-950: oklch(.129 .042 264.695); - --color-gray-50: oklch(.985 .002 247.839); - --color-gray-100: oklch(.967 .003 264.542); - --color-gray-200: oklch(.928 .006 264.531); - --color-gray-300: oklch(.872 .01 258.338); - --color-gray-400: oklch(.707 .022 261.325); - --color-gray-500: oklch(.551 .027 264.364); - --color-gray-600: oklch(.446 .03 256.802); - --color-gray-700: oklch(.373 .034 259.733); - --color-gray-800: oklch(.278 .033 256.848); - --color-gray-900: oklch(.21 .034 264.665); - --color-gray-950: oklch(.13 .028 261.692); - --color-zinc-50: oklch(.985 0 0); - --color-zinc-100: oklch(.967 .001 286.375); - --color-zinc-200: oklch(.92 .004 286.32); - --color-zinc-300: oklch(.871 .006 286.286); - --color-zinc-400: oklch(.705 .015 286.067); - --color-zinc-500: oklch(.552 .016 285.938); - --color-zinc-600: oklch(.442 .017 285.786); - --color-zinc-700: oklch(.37 .013 285.805); - --color-zinc-800: oklch(.274 .006 286.033); - --color-zinc-900: oklch(.21 .006 285.885); - --color-zinc-950: oklch(.141 .005 285.823); - --color-neutral-50: oklch(.985 0 0); - --color-neutral-100: oklch(.97 0 0); - --color-neutral-200: oklch(.922 0 0); - --color-neutral-300: oklch(.87 0 0); - --color-neutral-400: oklch(.708 0 0); - --color-neutral-500: oklch(.556 0 0); - --color-neutral-600: oklch(.439 0 0); - --color-neutral-700: oklch(.371 0 0); - --color-neutral-800: oklch(.269 0 0); - --color-neutral-900: oklch(.205 0 0); - --color-neutral-950: oklch(.145 0 0); - --color-stone-50: oklch(.985 .001 106.423); - --color-stone-100: oklch(.97 .001 106.424); - --color-stone-200: oklch(.923 .003 48.717); - --color-stone-300: oklch(.869 .005 56.366); - --color-stone-400: oklch(.709 .01 56.259); - --color-stone-500: oklch(.553 .013 58.071); - --color-stone-600: oklch(.444 .011 73.639); - --color-stone-700: oklch(.374 .01 67.558); - --color-stone-800: oklch(.268 .007 34.298); - --color-stone-900: oklch(.216 .006 56.043); - --color-stone-950: oklch(.147 .004 49.25); +" +@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, :before, :after, ::backdrop { + --tw-font-weight: initial; + } + } +} + +@layer theme { + :root, :host { + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, + sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", + monospace; --color-black: #000; - --color-white: #fff; - --spacing: .25rem; - --breakpoint-sm: 40rem; - --breakpoint-md: 48rem; - --breakpoint-lg: 64rem; - --breakpoint-xl: 80rem; - --breakpoint-2xl: 96rem; - --container-3xs: 16rem; - --container-2xs: 18rem; - --container-xs: 20rem; - --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-xl: 36rem; - --container-2xl: 42rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-5xl: 64rem; - --container-6xl: 72rem; - --container-7xl: 80rem; - --container-prose: 65ch; - --text-xs: .75rem; - --text-xs--line-height: calc(1 / .75); - --text-sm: .875rem; - --text-sm--line-height: calc(1.25 / .875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); --text-2xl: 1.5rem; --text-2xl--line-height: calc(2 / 1.5); - --text-3xl: 1.875rem; - --text-3xl--line-height: calc(2.25 / 1.875); - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; - --text-6xl: 3.75rem; - --text-6xl--line-height: 1; - --text-7xl: 4.5rem; - --text-7xl--line-height: 1; - --text-8xl: 6rem; - --text-8xl--line-height: 1; - --text-9xl: 8rem; - --text-9xl--line-height: 1; - --font-weight-thin: 100; - --font-weight-extralight: 200; - --font-weight-light: 300; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; --font-weight-bold: 700; - --font-weight-extrabold: 800; - --font-weight-black: 900; - --tracking-tighter: -.05em; - --tracking-tight: -.025em; - --tracking-normal: 0em; - --tracking-wide: .025em; - --tracking-wider: .05em; - --tracking-widest: .1em; - --leading-tight: 1.25; - --leading-snug: 1.375; - --leading-normal: 1.5; - --leading-relaxed: 1.625; - --leading-loose: 2; - --radius-xs: .125rem; - --radius-sm: .25rem; - --radius-md: .375rem; - --radius-lg: .5rem; - --radius-xl: .75rem; - --radius-2xl: 1rem; - --radius-3xl: 1.5rem; - --radius-4xl: 2rem; - --shadow-2xs: 0 1px #0000000d; - --shadow-xs: 0 1px 2px 0 #0000000d; - --shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a; - --shadow-md: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a; - --shadow-lg: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - --shadow-xl: 0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a; - --shadow-2xl: 0 25px 50px -12px #00000040; - --inset-shadow-2xs: inset 0 1px #0000000d; - --inset-shadow-xs: inset 0 1px 1px #0000000d; - --inset-shadow-sm: inset 0 2px 4px #0000000d; - --drop-shadow-xs: 0 1px 1px #0000000d; - --drop-shadow-sm: 0 1px 2px #00000026; - --drop-shadow-md: 0 3px 3px #0000001f; - --drop-shadow-lg: 0 4px 4px #00000026; - --drop-shadow-xl: 0 9px 7px #0000001a; - --drop-shadow-2xl: 0 25px 25px #00000026; - --ease-in: cubic-bezier(.4, 0, 1, 1); - --ease-out: cubic-bezier(0, 0, .2, 1); - --ease-in-out: cubic-bezier(.4, 0, .2, 1); - --animate-spin: spin 1s linear infinite; - --animate-ping: ping 1s cubic-bezier(0, 0, .2, 1) infinite; - --animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; - --animate-bounce: bounce 1s infinite; - --blur-xs: 4px; - --blur-sm: 8px; - --blur-md: 12px; - --blur-lg: 16px; - --blur-xl: 24px; - --blur-2xl: 40px; - --blur-3xl: 64px; - --perspective-dramatic: 100px; - --perspective-near: 300px; - --perspective-normal: 500px; - --perspective-midrange: 800px; - --perspective-distant: 1200px; - --aspect-video: 16 / 9; - --default-transition-duration: .15s; - --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); --default-font-family: var(--font-sans); - --default-font-feature-settings: var(--font-sans--font-feature-settings); - --default-font-variation-settings: var(--font-sans--font-variation-settings); --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); } } @@ -390,20 +44,16 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` -webkit-text-size-adjust: 100%; tab-size: 4; line-height: 1.5; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); + font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); font-feature-settings: var(--default-font-feature-settings, normal); font-variation-settings: var(--default-font-variation-settings, normal); -webkit-tap-highlight-color: transparent; } - body { - line-height: inherit; - } - hr { + height: 0; color: inherit; border-top-width: 1px; - height: 0; } abbr:where([title]) { @@ -460,7 +110,7 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` border-collapse: collapse; } - :-moz-focusring { + :-moz-focusring:where(:not(iframe)) { outline: auto; } @@ -522,7 +172,18 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` ::placeholder { opacity: 1; - color: color-mix(in oklab, currentColor 50%, transparent); + } + + @supports (not ((-webkit-appearance: -apple-pay-button))) or (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentColor; + } + + @supports (color: color-mix(in lab, red, red)) { + ::placeholder { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } } textarea { @@ -534,8 +195,8 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` } ::-webkit-date-and-time-value { - text-align: inherit; min-height: 1lh; + text-align: inherit; } ::-webkit-datetime-edit { @@ -582,6 +243,10 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` padding-block: 0; } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { box-shadow: none; } @@ -616,14 +281,20 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` } .text-black\\/50 { - color: color-mix(in oklab, var(--color-black) 50%, transparent); + color: #00000080; + } + + @supports (color: color-mix(in lab, red, red)) { + .text-black\\/50 { + color: color-mix(in oklab, var(--color-black) 50%, transparent); + } } .underline { text-decoration-line: underline; } - @media (width >= 96rem) { + @media (min-width: 96rem) { .\\32 xl\\:font-bold { --tw-font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold); @@ -631,47 +302,9 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = ` } } -@supports (-moz-orient: inline) { - @layer base { - *, :before, :after, ::backdrop { - --tw-font-weight: initial; - } - } -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -@keyframes ping { - 75%, 100% { - opacity: 0; - transform: scale(2); - } -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -@keyframes bounce { - 0%, 100% { - animation-timing-function: cubic-bezier(.8, 0, 1, 1); - transform: translateY(-25%); - } - - 50% { - animation-timing-function: cubic-bezier(0, 0, .2, 1); - transform: none; - } -} - @property --tw-font-weight { syntax: "*"; inherits: false -}" +} +" `; diff --git a/packages/@tailwindcss-postcss/src/ast.test.ts b/packages/@tailwindcss-postcss/src/ast.test.ts index dd7095428..8a2a5b019 100644 --- a/packages/@tailwindcss-postcss/src/ast.test.ts +++ b/packages/@tailwindcss-postcss/src/ast.test.ts @@ -84,7 +84,7 @@ it('should convert a Tailwind CSS AST into a PostCSS AST', () => { ` let ast = parse(input) - let transformedAst = cssAstToPostCssAst(ast) + let transformedAst = cssAstToPostCssAst(postcss, ast) expect(transformedAst.toString()).toMatchInlineSnapshot(` "@charset "UTF-8"; diff --git a/packages/@tailwindcss-postcss/src/ast.ts b/packages/@tailwindcss-postcss/src/ast.ts index 201dd4bf8..7ca9e2add 100644 --- a/packages/@tailwindcss-postcss/src/ast.ts +++ b/packages/@tailwindcss-postcss/src/ast.ts @@ -1,18 +1,67 @@ -import postcss, { - type ChildNode as PostCssChildNode, - type Container as PostCssContainerNode, - type Root as PostCssRoot, - type Source as PostcssSource, -} from 'postcss' +import type * as postcss from 'postcss' import { atRule, comment, decl, rule, type AstNode } from '../../tailwindcss/src/ast' +import { createLineTable, type LineTable } from '../../tailwindcss/src/source-maps/line-table' +import type { Source, SourceLocation } from '../../tailwindcss/src/source-maps/source' +import { DefaultMap } from '../../tailwindcss/src/utils/default-map' const EXCLAMATION_MARK = 0x21 -export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undefined): PostCssRoot { +export function cssAstToPostCssAst( + postcss: postcss.Postcss, + ast: AstNode[], + source?: postcss.Source, +): postcss.Root { + let inputMap = new DefaultMap((src) => { + return new postcss.Input(src.code, { + map: source?.input.map, + from: src.file ?? undefined, + }) + }) + + let lineTables = new DefaultMap((src) => createLineTable(src.code)) + let root = postcss.root() root.source = source - function transform(node: AstNode, parent: PostCssContainerNode) { + function toSource(loc: SourceLocation | undefined): postcss.Source | undefined { + // Use the fallback if this node has no location info in the AST + if (!loc) return + if (!loc[0]) return + + let table = lineTables.get(loc[0]) + let start = table.find(loc[1]) + let end = table.find(loc[2]) + + return { + input: inputMap.get(loc[0]), + start: { + line: start.line, + column: start.column + 1, + offset: loc[1], + }, + end: { + line: end.line, + column: end.column + 1, + offset: loc[2], + }, + } + } + + function updateSource(astNode: postcss.ChildNode, loc: SourceLocation | undefined) { + let source = toSource(loc) + + // The `source` property on PostCSS nodes must be defined if present because + // `toJSON()` reads each property and tries to read from source.input if it + // sees a `source` property. This means for a missing or otherwise absent + // source it must be *missing* from the object rather than just `undefined` + if (source) { + astNode.source = source + } else { + delete astNode.source + } + } + + function transform(node: AstNode, parent: postcss.Container) { // Declaration if (node.kind === 'declaration') { let astNode = postcss.decl({ @@ -20,14 +69,14 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef value: node.value ?? '', important: node.important, }) - astNode.source = source + updateSource(astNode, node.src) parent.append(astNode) } // Rule else if (node.kind === 'rule') { let astNode = postcss.rule({ selector: node.selector }) - astNode.source = source + updateSource(astNode, node.src) astNode.raws.semicolon = true parent.append(astNode) for (let child of node.nodes) { @@ -38,7 +87,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef // AtRule else if (node.kind === 'at-rule') { let astNode = postcss.atRule({ name: node.name.slice(1), params: node.params }) - astNode.source = source + updateSource(astNode, node.src) astNode.raws.semicolon = true parent.append(astNode) for (let child of node.nodes) { @@ -53,7 +102,7 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef // spaces. astNode.raws.left = '' astNode.raws.right = '' - astNode.source = source + updateSource(astNode, node.src) parent.append(astNode) } @@ -74,19 +123,39 @@ export function cssAstToPostCssAst(ast: AstNode[], source: PostcssSource | undef return root } -export function postCssAstToCssAst(root: PostCssRoot): AstNode[] { +export function postCssAstToCssAst(root: postcss.Root): AstNode[] { + let inputMap = new DefaultMap((input) => ({ + file: input.file ?? input.id ?? null, + code: input.css, + })) + + function toSource(node: postcss.ChildNode): SourceLocation | undefined { + let source = node.source + if (!source) return + + let input = source.input + if (!input) return + if (source.start === undefined) return + if (source.end === undefined) return + + return [inputMap.get(input), source.start.offset, source.end.offset] + } + function transform( - node: PostCssChildNode, + node: postcss.ChildNode, parent: Extract['nodes'], ) { // Declaration if (node.type === 'decl') { - parent.push(decl(node.prop, node.value, node.important)) + let astNode = decl(node.prop, node.value, node.important) + astNode.src = toSource(node) + parent.push(astNode) } // Rule else if (node.type === 'rule') { let astNode = rule(node.selector) + astNode.src = toSource(node) node.each((child) => transform(child, astNode.nodes)) parent.push(astNode) } @@ -94,6 +163,7 @@ export function postCssAstToCssAst(root: PostCssRoot): AstNode[] { // AtRule else if (node.type === 'atrule') { let astNode = atRule(`@${node.name}`, node.params) + astNode.src = toSource(node) node.each((child) => transform(child, astNode.nodes)) parent.push(astNode) } @@ -101,7 +171,9 @@ export function postCssAstToCssAst(root: PostCssRoot): AstNode[] { // Comment else if (node.type === 'comment') { if (node.text.charCodeAt(0) !== EXCLAMATION_MARK) return - parent.push(comment(node.text)) + let astNode = comment(node.text) + astNode.src = toSource(node) + parent.push(astNode) } // Unknown diff --git a/packages/@tailwindcss-postcss/src/fixtures/example-project/input.css b/packages/@tailwindcss-postcss/src/fixtures/example-project/input.css new file mode 100644 index 000000000..f1af498fc --- /dev/null +++ b/packages/@tailwindcss-postcss/src/fixtures/example-project/input.css @@ -0,0 +1 @@ +/* the content for this file is set in the tests */ diff --git a/packages/@tailwindcss-postcss/src/index.test.ts b/packages/@tailwindcss-postcss/src/index.test.ts index 50f72a13d..b393cc382 100644 --- a/packages/@tailwindcss-postcss/src/index.test.ts +++ b/packages/@tailwindcss-postcss/src/index.test.ts @@ -1,7 +1,10 @@ import dedent from 'dedent' -import { unlink, writeFile } from 'node:fs/promises' +import { mkdir, mkdtemp, readFile, rm, unlink, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'path' import postcss from 'postcss' -import { afterEach, beforeEach, describe, expect, test } from 'vitest' +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' +import { pretty } from '../../tailwindcss/src/test-utils/run' import tailwindcss from './index' // We give this file path to PostCSS for processing. @@ -17,6 +20,16 @@ function inputCssFilePath() { const css = dedent +async function run(plugin: any, from: string, input: string): Promise { + let ast = postcss.parse(input) + for (let runner of plugin.plugins) { + if (runner.Once) { + await runner.Once(ast, { postcss, result: { opts: { from }, messages: [] } }) + } + } + return ast.toString() +} + test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => { let processor = postcss([ tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), @@ -24,7 +37,7 @@ test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => { let result = await processor.process(`@import 'tailwindcss'`, { from: inputCssFilePath() }) - expect(result.css.trim()).toMatchSnapshot() + expect(pretty(result.css)).toMatchSnapshot() // Check for dependency messages expect(result.messages).toContainEqual({ @@ -41,7 +54,7 @@ test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => { }) expect(result.messages).toContainEqual({ type: 'dir-dependency', - dir: expect.stringMatching(/example-project[\/|\\]src$/g), + dir: expect.stringMatching(/example-project[/|\\]src$/g), glob: expect.stringMatching(/^\*\*\/\*/g), parent: expect.any(String), plugin: expect.any(String), @@ -70,8 +83,9 @@ test('output is optimized by Lightning CSS', async () => { { from: inputCssFilePath() }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - "@layer utilities { + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + @layer utilities { .foo { color: #000; } @@ -79,7 +93,8 @@ test('output is optimized by Lightning CSS', async () => { .bar { color: red; } - }" + } + " `) }) @@ -90,7 +105,7 @@ test('@apply can be used without emitting the theme in the CSS file', async () = let result = await processor.process( css` - @import 'tailwindcss/theme.css' theme(reference); + @reference 'tailwindcss/theme.css'; .foo { @apply text-red-500; } @@ -98,22 +113,31 @@ test('@apply can be used without emitting the theme in the CSS file', async () = { from: inputCssFilePath() }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - ".foo { - color: var(--color-red-500); - }" + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .foo { + color: var(--color-red-500, oklch(63.7% .237 25.331)); + } + " `) }) describe('processing without specifying a base path', () => { - let filepath = `${process.cwd()}/my-test-file.html` + let filepath: string + let dir: string - beforeEach(() => - writeFile(filepath, `
`), - ) + beforeEach(async () => { + dir = await mkdtemp(path.join(tmpdir(), 'tw-postcss')) + await mkdir(dir, { recursive: true }) + filepath = path.join(dir, 'my-test-file.html') + await writeFile(filepath, `
`) + }) afterEach(() => unlink(filepath)) test('the current working directory is used by default', async () => { + using spy = vi.spyOn(process, 'cwd') + spy.mockReturnValue(dir) + let processor = postcss([tailwindcss({ optimize: { minify: false } })]) let result = await processor.process(`@import "tailwindcss"`, { from: inputCssFilePath() }) @@ -131,6 +155,18 @@ describe('processing without specifying a base path', () => { }) }) +test('fallback to `base` directory when `result.opts.from` is not provided', async () => { + using _ = vi.spyOn(console, 'warn').mockImplementation(() => {}) + + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) + + let result = await processor.process(`@import 'tailwindcss'`) + + expect(result.css.length).toBeGreaterThan(0) +}) + describe('plugins', () => { test('local CJS plugin', async () => { let processor = postcss([ @@ -145,8 +181,9 @@ describe('plugins', () => { { from: inputCssFilePath() }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - ".underline { + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .underline { text-decoration-line: underline; } @@ -158,7 +195,8 @@ describe('plugins', () => { .hocus\\:underline:focus, .hocus\\:underline:hover { text-decoration-line: underline; - }" + } + " `) }) @@ -175,8 +213,9 @@ describe('plugins', () => { { from: `${__dirname}/fixtures/another-project/input.css` }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - ".underline { + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .underline { text-decoration-line: underline; } @@ -188,7 +227,8 @@ describe('plugins', () => { .hocus\\:underline:focus, .hocus\\:underline:hover { text-decoration-line: underline; - }" + } + " `) }) @@ -205,8 +245,9 @@ describe('plugins', () => { { from: inputCssFilePath() }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - ".underline { + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .underline { text-decoration-line: underline; } @@ -218,7 +259,8 @@ describe('plugins', () => { .hocus\\:underline:focus, .hocus\\:underline:hover { text-decoration-line: underline; - }" + } + " `) }) }) @@ -241,10 +283,66 @@ test('bail early when Tailwind is not used', async () => { // didn't use `@tailwind utilities` we didn't scan for utilities. expect(result.css).not.toContain('.underline {') - expect(result.css.trim()).toMatchInlineSnapshot(` - ".custom-css { + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .custom-css { color: red; - }" + } + " + `) +}) + +test('handle CSS when only using a `@reference` (we should not bail early)', async () => { + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) + + let result = await processor.process( + css` + @reference "tailwindcss/theme.css"; + + .foo { + @variant md { + bar: baz; + } + } + `, + { from: inputCssFilePath() }, + ) + + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + @media (min-width: 48rem) { + .foo { + bar: baz; + } + } + " + `) +}) + +test('handle CSS when using a `@variant` using variants that do not rely on the `@theme`', async () => { + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) + + let result = await processor.process( + css` + .foo { + @variant data-is-hoverable { + bar: baz; + } + } + `, + { from: inputCssFilePath() }, + ) + + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .foo[data-is-hoverable] { + bar: baz; + } + " `) }) @@ -279,30 +377,142 @@ test('runs `Once` plugins in the right order', async () => { { from: inputCssFilePath() }, ) - expect(result.css.trim()).toMatchInlineSnapshot(` - ":root { - --color-red-500: red; - } - + expect(pretty(result.css)).toMatchInlineSnapshot(` + " .custom-css { color: red; - }" + } + " `) - expect(before).toMatchInlineSnapshot(` - "@theme { + expect(pretty(before)).toMatchInlineSnapshot(` + " + @theme { --color-red-500: red; } .custom-css { color: theme(--color-red-500); - }" - `) - expect(after).toMatchInlineSnapshot(` - ":root { - --color-red-500: red; } - + " + `) + expect(pretty(after)).toMatchInlineSnapshot(` + " .custom-css { color: red; - }" + } + " `) }) + +describe('concurrent builds', () => { + let dir: string + beforeEach(async () => { + dir = await mkdtemp(path.join(tmpdir(), 'tw-postcss')) + await writeFile(path.join(dir, 'index.html'), `
`) + await writeFile( + path.join(dir, 'index.css'), + css` + @import './dependency.css'; + `, + ) + await writeFile( + path.join(dir, 'dependency.css'), + css` + @tailwind utilities; + `, + ) + }) + afterEach(() => rm(dir, { recursive: true, force: true })) + + test('does experience a race-condition when calling the plugin two times for the same change', async () => { + using spy = vi.spyOn(process, 'cwd') + spy.mockReturnValue(dir) + + let from = path.join(dir, 'index.css') + let input = (await readFile(path.join(dir, 'index.css'))).toString() + + let plugin = tailwindcss({ optimize: { minify: false } }) + + let result = await run(plugin, from, input) + + expect(result).toContain('.underline') + + // Ensure that the mtime is updated + await new Promise((resolve) => setTimeout(resolve, 100)) + await writeFile( + path.join(dir, 'dependency.css'), + css` + @tailwind utilities; + .red { + color: red; + } + `, + ) + + let promise1 = run(plugin, from, input) + let promise2 = run(plugin, from, input) + + expect(await promise1).toContain('.red') + expect(await promise2).toContain('.red') + }) +}) + +test('rebuilds when the input CSS changes even if the `from` file on disk did not', async () => { + let dir = await mkdtemp(path.join(tmpdir(), 'tw-postcss')) + // The `from` file exists on disk with a stable mtime. The input CSS is handed + // to the plugin directly (as a preprocessor like Sass would produce it), so it + // can change without `from`'s mtime ever changing. + await writeFile(path.join(dir, 'index.css'), '') + + let from = path.join(dir, 'index.css') + let plugin = tailwindcss({ base: dir, optimize: { minify: false } }) + + let first = await run( + plugin, + from, + css` + @tailwind utilities; + .first { + @apply underline; + } + `, + ) + let second = await run( + plugin, + from, + css` + @tailwind utilities; + .second { + @apply line-through; + } + `, + ) + + expect(first).toContain('.first') + expect(second).toContain('.second') + + await rm(dir, { recursive: true, force: true }) +}) + +test('does not register the input file as a dependency, even if it is passed in as relative path', async () => { + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) + + let result = await processor.process(`@tailwind utilities`, { from: './input.css' }) + + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + .underline { + text-decoration-line: underline; + } + " + `) + + // Check for dependency messages + expect(result.messages).not.toContainEqual({ + type: 'dependency', + file: expect.stringMatching(/input.css$/g), + parent: expect.any(String), + plugin: expect.any(String), + }) +}) diff --git a/packages/@tailwindcss-postcss/src/index.ts b/packages/@tailwindcss-postcss/src/index.ts index c69b9a875..a4a8d9326 100644 --- a/packages/@tailwindcss-postcss/src/index.ts +++ b/packages/@tailwindcss-postcss/src/index.ts @@ -1,11 +1,17 @@ import QuickLRU from '@alloc/quick-lru' -import { compileAst, env, Features, Instrumentation } from '@tailwindcss/node' +import { + compileAst, + env, + Features, + Instrumentation, + optimize as optimizeCss, + Polyfills, +} from '@tailwindcss/node' import { clearRequireCache } from '@tailwindcss/node/require-cache' import { Scanner } from '@tailwindcss/oxide' -import { Features as LightningCssFeatures, transform } from 'lightningcss' import fs from 'node:fs' import path, { relative } from 'node:path' -import postcss, { type AcceptedPlugin, type PluginCreator } from 'postcss' +import type { AcceptedPlugin, PluginCreator, Postcss, Root } from 'postcss' import { toCss, type AstNode } from '../../tailwindcss/src/ast' import { cssAstToPostCssAst, postCssAstToCssAst } from './ast' import fixRelativePathsPlugin from './postcss-fix-relative-paths' @@ -14,20 +20,22 @@ const DEBUG = env.DEBUG interface CacheEntry { mtimes: Map - compiler: null | Awaited> + inputCss: string + compiler: null | ReturnType scanner: null | Scanner tailwindCssAst: AstNode[] - cachedPostCssAst: postcss.Root - optimizedPostCssAst: postcss.Root + cachedPostCssAst: Root + optimizedPostCssAst: Root fullRebuildPaths: string[] } const cache = new QuickLRU({ maxSize: 50 }) -function getContextFromCache(inputFile: string, opts: PluginOptions): CacheEntry { +function getContextFromCache(postcss: Postcss, inputFile: string, opts: PluginOptions): CacheEntry { let key = `${inputFile}:${opts.base ?? ''}:${JSON.stringify(opts.optimize)}` if (cache.has(key)) return cache.get(key)! let entry = { mtimes: new Map(), + inputCss: '', compiler: null, scanner: null, @@ -42,16 +50,30 @@ function getContextFromCache(inputFile: string, opts: PluginOptions): CacheEntry } export type PluginOptions = { - // The base directory to scan for class candidates. + /** + * The base directory to scan for class candidates. + * + * Defaults to the current working directory. + */ base?: string - // Optimize and minify the output CSS. + /** + * Optimize and minify the output CSS. + */ optimize?: boolean | { minify?: boolean } + + /** + * Enable or disable asset URL rewriting. + * + * Defaults to `true`. + */ + transformAssetUrls?: boolean } function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { let base = opts.base ?? process.cwd() let optimize = opts.optimize ?? process.env.NODE_ENV === 'production' + let shouldRewriteUrls = opts.transformAssetUrls ?? true return { postcssPlugin: '@tailwindcss/postcss', @@ -63,10 +85,11 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { { postcssPlugin: 'tailwindcss', - async Once(root, { result }) { + async Once(root, { result, postcss }) { using I = new Instrumentation() let inputFile = result.opts.from ?? '' + let isCSSModuleFile = inputFile.endsWith('.module.css') DEBUG && I.start(`[@tailwindcss/postcss] ${relative(base, inputFile)}`) @@ -77,10 +100,13 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { root.walkAtRules((node) => { if ( node.name === 'import' || + node.name === 'reference' || node.name === 'theme' || + node.name === 'variant' || node.name === 'config' || node.name === 'plugin' || - node.name === 'apply' + node.name === 'apply' || + node.name === 'tailwind' ) { canBail = false return false @@ -90,8 +116,12 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { DEBUG && I.end('Quick bail check') } - let context = getContextFromCache(inputFile, opts) - let inputBasePath = path.dirname(path.resolve(inputFile)) + let context = getContextFromCache(postcss, inputFile, opts) + let inputBasePath = inputFile ? path.dirname(path.resolve(inputFile)) : base + + // Whether this is the first build or not, if it is, then we can + // optimize the build by not creating the compiler until we need it. + let isInitialBuild = context.compiler === null async function createCompiler() { DEBUG && I.start('Setup compiler') @@ -107,10 +137,14 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { DEBUG && I.start('Create compiler') let compiler = await compileAst(ast, { + from: result.opts.from, base: inputBasePath, - onDependency: (path) => { - context.fullRebuildPaths.push(path) - }, + shouldRewriteUrls, + onDependency: (path) => context.fullRebuildPaths.push(path), + // In CSS Module files, we have to disable the `@property` polyfill since these will + // emit global `*` rules which are considered to be non-pure and will cause builds + // to fail. + polyfills: isCSSModuleFile ? Polyfills.All ^ Polyfills.AtProperty : Polyfills.All, }) DEBUG && I.end('Create compiler') @@ -118,23 +152,203 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { return compiler } - // Whether this is the first build or not, if it is, then we can - // optimize the build by not creating the compiler until we need it. - let isInitialBuild = context.compiler === null + try { + // Setup the compiler if it doesn't exist yet. This way we can + // guarantee a `build()` function is available. + context.compiler ??= createCompiler() - // Setup the compiler if it doesn't exist yet. This way we can - // guarantee a `build()` function is available. - context.compiler ??= await createCompiler() + if ((await context.compiler).features === Features.None) { + return + } - if (context.compiler.features === Features.None) { - return - } + let rebuildStrategy: 'full' | 'incremental' = 'incremental' - let rebuildStrategy: 'full' | 'incremental' = 'incremental' + // Track file modification times to CSS files + DEBUG && I.start('Register full rebuild paths') + { + for (let file of context.fullRebuildPaths) { + result.messages.push({ + type: 'dependency', + plugin: '@tailwindcss/postcss', + file: path.resolve(file), + parent: result.opts.from, + }) + } - // Track file modification times to CSS files - DEBUG && I.start('Register full rebuild paths') - { + let files = result.messages.flatMap((message) => { + if (message.type !== 'dependency') return [] + return message.file + }) + files.push(inputFile) + + for (let file of files) { + let changedTime = fs.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? null + if (changedTime === null) { + if (file === inputFile) { + rebuildStrategy = 'full' + } + continue + } + + let prevTime = context.mtimes.get(file) + if (prevTime === changedTime) continue + + rebuildStrategy = 'full' + context.mtimes.set(file, changedTime) + } + + // The mtimes above only track files on disk. When the input CSS is + // generated by another tool (e.g. Sass) and passed to `process()`, + // it can change without any tracked file's mtime changing, so also + // rebuild when the input CSS itself differs from the last build. + let inputCss = root.source?.input.css ?? root.toString() + if (context.inputCss !== inputCss) { + rebuildStrategy = 'full' + context.inputCss = inputCss + } + } + DEBUG && I.end('Register full rebuild paths') + + if ( + rebuildStrategy === 'full' && + // We can reuse the compiler if it was created during the + // initial build. If it wasn't, we need to create a new one. + !isInitialBuild + ) { + context.compiler = createCompiler() + } + + let compiler = await context.compiler + + if (context.scanner === null || rebuildStrategy === 'full') { + DEBUG && I.start('Setup scanner') + let sources = (() => { + // Disable auto source detection + if (compiler.root === 'none') { + return [] + } + + // No root specified, use the base directory + if (compiler.root === null) { + return [{ base, pattern: '**/*', negated: false }] + } + + // Use the specified root + return [{ ...compiler.root, negated: false }] + })().concat(compiler.sources) + + // Look for candidates used to generate the CSS + context.scanner = new Scanner({ sources }) + DEBUG && I.end('Setup scanner') + } + + DEBUG && I.start('Scan for candidates') + let candidates = compiler.features & Features.Utilities ? context.scanner.scan() : [] + DEBUG && I.end('Scan for candidates') + + if (compiler.features & Features.Utilities) { + DEBUG && I.start('Register dependency messages') + // Add all found files as direct dependencies + // Note: With Turbopack, the input file might not be a resolved path + let resolvedInputFile = path.resolve(base, inputFile) + for (let file of context.scanner.files) { + let absolutePath = path.resolve(file) + // The CSS file cannot be a dependency of itself + if (absolutePath === resolvedInputFile) { + continue + } + result.messages.push({ + type: 'dependency', + plugin: '@tailwindcss/postcss', + file: absolutePath, + parent: result.opts.from, + }) + } + + // Register dependencies so changes in `base` cause a rebuild while + // giving tools like Vite or Parcel a glob that can be used to limit + // the files that cause a rebuild to only those that match it. + for (let { base: globBase, pattern } of context.scanner.globs) { + // Avoid adding a dependency on the base directory itself, since it + // causes Next.js to start an endless recursion if the `distDir` is + // configured to anything other than the default `.next` dir. + if (pattern === '*' && base === globBase) { + continue + } + + if (pattern === '') { + result.messages.push({ + type: 'dependency', + plugin: '@tailwindcss/postcss', + file: path.resolve(globBase), + parent: result.opts.from, + }) + } else { + result.messages.push({ + type: 'dir-dependency', + plugin: '@tailwindcss/postcss', + dir: path.resolve(globBase), + glob: pattern, + parent: result.opts.from, + }) + } + } + DEBUG && I.end('Register dependency messages') + } + + DEBUG && I.start('Build utilities') + let tailwindCssAst = compiler.build(candidates) + DEBUG && I.end('Build utilities') + + if (context.tailwindCssAst !== tailwindCssAst) { + if (optimize) { + DEBUG && I.start('Optimization') + + DEBUG && I.start('AST -> CSS') + let css = toCss(tailwindCssAst) + DEBUG && I.end('AST -> CSS') + + DEBUG && I.start('Lightning CSS') + let optimized = optimizeCss(css, { + minify: typeof optimize === 'object' ? optimize.minify : true, + }) + DEBUG && I.end('Lightning CSS') + + DEBUG && I.start('CSS -> PostCSS AST') + context.optimizedPostCssAst = postcss.parse(optimized.code, result.opts) + DEBUG && I.end('CSS -> PostCSS AST') + + DEBUG && I.end('Optimization') + } else { + // Convert our AST to a PostCSS AST + DEBUG && I.start('Transform Tailwind CSS AST into PostCSS AST') + context.cachedPostCssAst = cssAstToPostCssAst(postcss, tailwindCssAst, root.source) + DEBUG && I.end('Transform Tailwind CSS AST into PostCSS AST') + } + } + + context.tailwindCssAst = tailwindCssAst + + DEBUG && I.start('Update PostCSS AST') + root.removeAll() + root.append( + optimize + ? context.optimizedPostCssAst.clone().nodes + : context.cachedPostCssAst.clone().nodes, + ) + + // Trick PostCSS into thinking the indent is 2 spaces, so it uses that + // as the default instead of 4. + root.raws.indent = ' ' + DEBUG && I.end('Update PostCSS AST') + + DEBUG && I.end(`[@tailwindcss/postcss] ${relative(base, inputFile)}`) + } catch (error) { + // An error requires a full rebuild to fix + context.compiler = null + + // Ensure all dependencies we have collected thus far are included so that the rebuild + // is correctly triggered for (let file of context.fullRebuildPaths) { result.messages.push({ type: 'dependency', @@ -144,193 +358,20 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { }) } - let files = result.messages.flatMap((message) => { - if (message.type !== 'dependency') return [] - return message.file - }) - files.push(inputFile) + // We found that throwing the error will cause PostCSS to no longer watch for changes + // in some situations so we instead log the error and continue with an empty stylesheet. + console.error(error) - for (let file of files) { - let changedTime = fs.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? null - if (changedTime === null) { - if (file === inputFile) { - rebuildStrategy = 'full' - } - continue - } - - let prevTime = context.mtimes.get(file) - if (prevTime === changedTime) continue - - rebuildStrategy = 'full' - context.mtimes.set(file, changedTime) - } - } - DEBUG && I.end('Register full rebuild paths') - - if ( - rebuildStrategy === 'full' && - // We can re-use the compiler if it was created during the - // initial build. If it wasn't, we need to create a new one. - !isInitialBuild - ) { - context.compiler = await createCompiler() - } - - if (context.scanner === null || rebuildStrategy === 'full') { - DEBUG && I.start('Setup scanner') - let sources = (() => { - // Disable auto source detection - if (context.compiler.root === 'none') { - return [] - } - - // No root specified, use the base directory - if (context.compiler.root === null) { - return [{ base, pattern: '**/*' }] - } - - // Use the specified root - return [context.compiler.root] - })().concat(context.compiler.globs) - - // Look for candidates used to generate the CSS - context.scanner = new Scanner({ sources }) - DEBUG && I.end('Setup scanner') - } - - DEBUG && I.start('Scan for candidates') - let candidates = - context.compiler.features & Features.Utilities ? context.scanner.scan() : [] - DEBUG && I.end('Scan for candidates') - - if (context.compiler.features & Features.Utilities) { - DEBUG && I.start('Register dependency messages') - // Add all found files as direct dependencies - for (let file of context.scanner.files) { - result.messages.push({ - type: 'dependency', - plugin: '@tailwindcss/postcss', - file: path.resolve(file), - parent: result.opts.from, - }) + if (error && typeof error === 'object' && 'message' in error) { + throw root.error(`${error.message}`) } - // Register dependencies so changes in `base` cause a rebuild while - // giving tools like Vite or Parcel a glob that can be used to limit - // the files that cause a rebuild to only those that match it. - for (let { base: globBase, pattern } of context.scanner.globs) { - // Avoid adding a dependency on the base directory itself, since it - // causes Next.js to start an endless recursion if the `distDir` is - // configured to anything other than the default `.next` dir. - if (pattern === '*' && base === globBase) { - continue - } - - if (pattern === '') { - result.messages.push({ - type: 'dependency', - plugin: '@tailwindcss/postcss', - file: path.resolve(globBase), - parent: result.opts.from, - }) - } else { - result.messages.push({ - type: 'dir-dependency', - plugin: '@tailwindcss/postcss', - dir: path.resolve(globBase), - glob: pattern, - parent: result.opts.from, - }) - } - } - DEBUG && I.end('Register dependency messages') + throw root.error(`${error}`) } - - DEBUG && I.start('Build utilities') - let tailwindCssAst = context.compiler.build(candidates) - DEBUG && I.end('Build utilities') - - if (context.tailwindCssAst !== tailwindCssAst) { - if (optimize) { - DEBUG && I.start('Optimization') - - DEBUG && I.start('AST -> CSS') - let css = toCss(tailwindCssAst) - DEBUG && I.end('AST -> CSS') - - DEBUG && I.start('Lightning CSS') - let ast = optimizeCss(css, { - minify: typeof optimize === 'object' ? optimize.minify : true, - }) - DEBUG && I.end('Lightning CSS') - - DEBUG && I.start('CSS -> PostCSS AST') - context.optimizedPostCssAst = postcss.parse(ast, result.opts) - DEBUG && I.end('CSS -> PostCSS AST') - - DEBUG && I.end('Optimization') - } else { - // Convert our AST to a PostCSS AST - DEBUG && I.start('Transform Tailwind CSS AST into PostCSS AST') - context.cachedPostCssAst = cssAstToPostCssAst(tailwindCssAst, root.source) - DEBUG && I.end('Transform Tailwind CSS AST into PostCSS AST') - } - } - - context.tailwindCssAst = tailwindCssAst - - DEBUG && I.start('Update PostCSS AST') - root.removeAll() - root.append( - optimize - ? context.optimizedPostCssAst.clone().nodes - : context.cachedPostCssAst.clone().nodes, - ) - - // Trick PostCSS into thinking the indent is 2 spaces, so it uses that - // as the default instead of 4. - root.raws.indent = ' ' - DEBUG && I.end('Update PostCSS AST') - - DEBUG && I.end(`[@tailwindcss/postcss] ${relative(base, inputFile)}`) }, }, ], } } -function optimizeCss( - input: string, - { file = 'input.css', minify = false }: { file?: string; minify?: boolean } = {}, -) { - function optimize(code: Buffer | Uint8Array) { - return transform({ - filename: file, - code, - minify, - sourceMap: false, - drafts: { - customMedia: true, - }, - nonStandard: { - deepSelectorCombinator: true, - }, - include: LightningCssFeatures.Nesting, - exclude: LightningCssFeatures.LogicalProperties | LightningCssFeatures.DirSelector, - targets: { - safari: (16 << 16) | (4 << 8), - ios_saf: (16 << 16) | (4 << 8), - firefox: 128 << 16, - chrome: 111 << 16, - }, - errorRecovery: true, - }).code - } - - // Running Lightning CSS twice to ensure that adjacent rules are merged after - // nesting is applied. This creates a more optimized output. - return optimize(optimize(Buffer.from(input))).toString() -} - export default Object.assign(tailwindcss, { postcss: true }) as PluginCreator diff --git a/packages/@tailwindcss-postcss/src/postcss-fix-relative-paths/index.test.ts b/packages/@tailwindcss-postcss/src/postcss-fix-relative-paths/index.test.ts index d2f72f664..fd90c483e 100644 --- a/packages/@tailwindcss-postcss/src/postcss-fix-relative-paths/index.test.ts +++ b/packages/@tailwindcss-postcss/src/postcss-fix-relative-paths/index.test.ts @@ -4,6 +4,7 @@ import postcss from 'postcss' import atImport from 'postcss-import' import { describe, expect, test } from 'vitest' import fixRelativePathsPlugin from '.' +import { pretty } from '../../../tailwindcss/src/test-utils/run' describe('fixRelativePathsPlugin', () => { test('rewrites @source and @plugin to be relative to the initial css file', async () => { @@ -14,11 +15,13 @@ describe('fixRelativePathsPlugin', () => { let result = await processor.process(css, { from: cssPath }) - expect(result.css.trim()).toMatchInlineSnapshot(` - "@source "../../example-project/src/**/*.ts"; + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + @source "../../example-project/src/**/*.ts"; @source "!../../example-project/src/**/*.ts"; @plugin "../../example-project/src/plugin.js"; - @plugin "../../example-project/src/what\\"s-this.js";" + @plugin "../../example-project/src/what\\"s-this.js"; + " `) }) @@ -30,11 +33,13 @@ describe('fixRelativePathsPlugin', () => { let result = await processor.process(css, { from: cssPath }) - expect(result.css.trim()).toMatchInlineSnapshot(` - "@plugin "/absolute/paths"; + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + @plugin "/absolute/paths"; @plugin "C:\\Program Files\\HAL 9000"; @plugin "\\\\Media\\Pictures\\Worth\\1000 words"; - @plugin "some-node-dep";" + @plugin "some-node-dep"; + " `) }) @@ -46,13 +51,16 @@ describe('fixRelativePathsPlugin', () => { let result = await processor.process(css, { from: cssPath }) - expect(result.css.trim()).toMatchInlineSnapshot(` - "@plugin './plugin-in-sibling.ts'; + expect(pretty(result.css)).toMatchInlineSnapshot(` + " + @plugin './plugin-in-sibling.ts'; @plugin '../plugin-in-sibling.ts'; @plugin 'plugin-in-sibling'; + @plugin './plugin-in-root.ts'; @plugin '../plugin-in-root.ts'; - @plugin 'plugin-in-root';" + @plugin 'plugin-in-root'; + " `) }) }) diff --git a/packages/@tailwindcss-postcss/tsconfig.json b/packages/@tailwindcss-postcss/tsconfig.json index 6ae022f65..d2131cbeb 100644 --- a/packages/@tailwindcss-postcss/tsconfig.json +++ b/packages/@tailwindcss-postcss/tsconfig.json @@ -1,3 +1,6 @@ { "extends": "../tsconfig.base.json", + "compilerOptions": { + "verbatimModuleSyntax": false, + }, } diff --git a/packages/@tailwindcss-standalone/package.json b/packages/@tailwindcss-standalone/package.json index 704ed563e..24f8c93c7 100644 --- a/packages/@tailwindcss-standalone/package.json +++ b/packages/@tailwindcss-standalone/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/standalone", - "version": "4.0.0-beta.7", + "version": "4.3.3", "private": true, "description": "Standalone CLI for Tailwind CSS", "license": "MIT", @@ -26,30 +26,30 @@ ], "dependencies": { "@tailwindcss/aspect-ratio": "^0.4.2", - "@tailwindcss/cli": "workspace:^", - "@tailwindcss/forms": "^0.5.9", - "@tailwindcss/typography": "^0.5.15", + "@tailwindcss/cli": "workspace:*", + "@tailwindcss/forms": "^0.5.11", + "@tailwindcss/typography": "^0.5.20", "detect-libc": "1.0.3", - "enhanced-resolve": "^5.17.1", - "tailwindcss": "workspace:^" + "enhanced-resolve": "catalog:", + "tailwindcss": "workspace:*" }, "__notes": "These binary packages must be included so Bun can build the CLI for all supported platforms. We also rely on Lightning CSS and Parcel being patched so Bun can statically analyze the executables.", "devDependencies": { - "@parcel/watcher-darwin-arm64": "^2.5.0", - "@parcel/watcher-darwin-x64": "^2.5.0", - "@parcel/watcher-linux-arm64-glibc": "^2.5.0", - "@parcel/watcher-linux-arm64-musl": "^2.5.0", - "@parcel/watcher-linux-x64-glibc": "^2.5.0", - "@parcel/watcher-linux-x64-musl": "^2.5.0", - "@parcel/watcher-win32-x64": "^2.5.0", - "@types/bun": "^1.1.13", - "bun": "1.1.29", - "lightningcss-darwin-arm64": "^1.25.1", - "lightningcss-darwin-x64": "^1.25.1", - "lightningcss-linux-arm64-gnu": "^1.25.1", - "lightningcss-linux-arm64-musl": "^1.25.1", - "lightningcss-linux-x64-gnu": "^1.25.1", - "lightningcss-linux-x64-musl": "^1.25.1", - "lightningcss-win32-x64-msvc": "^1.25.1" + "@parcel/watcher-darwin-arm64": "catalog:", + "@parcel/watcher-darwin-x64": "catalog:", + "@parcel/watcher-linux-arm64-glibc": "catalog:", + "@parcel/watcher-linux-arm64-musl": "catalog:", + "@parcel/watcher-linux-x64-glibc": "catalog:", + "@parcel/watcher-linux-x64-musl": "catalog:", + "@parcel/watcher-win32-x64": "catalog:", + "@types/bun": "^1.3.14", + "bun": "^1.3.14", + "lightningcss-darwin-arm64": "catalog:", + "lightningcss-darwin-x64": "catalog:", + "lightningcss-linux-arm64-gnu": "catalog:", + "lightningcss-linux-arm64-musl": "catalog:", + "lightningcss-linux-x64-gnu": "catalog:", + "lightningcss-linux-x64-musl": "catalog:", + "lightningcss-win32-x64-msvc": "catalog:" } } diff --git a/packages/@tailwindcss-standalone/scripts/build.ts b/packages/@tailwindcss-standalone/scripts/build.ts index 83f3b20a9..0aee26435 100644 --- a/packages/@tailwindcss-standalone/scripts/build.ts +++ b/packages/@tailwindcss-standalone/scripts/build.ts @@ -1,4 +1,3 @@ -import { $ } from 'bun' import { createHash } from 'node:crypto' import { mkdir, readFile, writeFile } from 'node:fs/promises' import * as path from 'node:path' @@ -6,66 +5,107 @@ import { fileURLToPath } from 'node:url' const __dirname = fileURLToPath(new URL('.', import.meta.url)) -async function buildForPlatform(triple: string, outfile: string) { - // We wrap this in a retry because occasionally the atomic rename fails for some reason - for (let i = 0; i < 5; ++i) { - try { - return await $`bun build --compile --target=${triple} ./src/index.ts --outfile=${outfile}` - } catch (err) { - if (i < 5) continue +// Workaround for Bun binary downloads failing on Windows CI when +// USERPROFILE is passed through by Turborepo. +// +// Unfortunately, setting this at runtime doesn't appear to work so we have to +// spawn a new process without the env var. +if (process.env.NESTED_BUILD !== '1' && process.env.USERPROFILE && process.env.USERPROFILE !== '') { + let result = await Bun.$`bun ${fileURLToPath(import.meta.url)}`.env({ + USERPROFILE: '', + NESTED_BUILD: '1', + }) - throw new Error(`Failed to build for platform ${triple}`, { cause: err }) - } - } + process.exit(result.exitCode) } -async function build(triple: string, file: string) { - let start = process.hrtime.bigint() +// We use baseline builds for all x64 platforms to ensure compatibility with +// older hardware. +let builds: { target: Bun.Build.Target; name: string }[] = [ + { name: 'tailwindcss-linux-arm64', target: 'bun-linux-arm64' }, + { name: 'tailwindcss-linux-arm64-musl', target: 'bun-linux-arm64-musl' }, + // @ts-expect-error: Either the types are wrong or the runtime needs to be updated + // to accept a `-glibc` at the end like the types suggest. + { name: 'tailwindcss-linux-x64', target: 'bun-linux-x64-baseline' }, + { name: 'tailwindcss-linux-x64-musl', target: 'bun-linux-x64-baseline-musl' }, + { name: 'tailwindcss-macos-arm64', target: 'bun-darwin-arm64' }, + { name: 'tailwindcss-macos-x64', target: 'bun-darwin-x64-baseline' }, + { name: 'tailwindcss-windows-x64.exe', target: 'bun-windows-x64-baseline' }, +] - let outfile = path.resolve(__dirname, `../dist/${file}`) +let summary: { target: Bun.Build.Target; name: string; sum: string }[] = [] - await buildForPlatform(triple, outfile) +// Build platform binaries and checksum them. +let start = process.hrtime.bigint() +for (let { target, name } of builds) { + let outfile = path.resolve(__dirname, `../dist/${name}`) - await new Promise((resolve) => setTimeout(resolve, 100)) + let result = await Bun.build({ + entrypoints: ['./src/index.ts'], + target: 'node', + minify: { + whitespace: false, + syntax: true, + identifiers: false, + keepNames: true, + }, + + define: { + // This ensures only necessary binaries are bundled for linux targets + // It reduces binary size since no runtime selection is necessary + 'process.env.PLATFORM_LIBC': JSON.stringify(target.includes('-musl') ? 'musl' : 'glibc'), + + // This prevents the WASI build from being bundled with the binary + 'process.env.NAPI_RS_FORCE_WASI': JSON.stringify(''), + + // This simplifies the Oxide loading code a small amount + 'process.env.NAPI_RS_NATIVE_LIBRARY_PATH': JSON.stringify(''), + }, + + compile: { + target, + outfile, + + // Disable .env loading + autoloadDotenv: false, + + // Disable bunfig.toml loading + autoloadBunfig: false, + }, + + plugins: [ + { + name: 'tailwindcss-plugin', + setup(build) { + build.onLoad({ filter: /tailwindcss-oxide\.wasi\.cjs$/ }, async (args) => { + return { contents: '' } + }) + }, + }, + ], + }) + + let entry = result.outputs.find((output) => output.kind === 'entry-point') + if (!entry) throw new Error(`Build failed for ${target}`) let content = await readFile(outfile) - let sum = createHash('sha256').update(content).digest('hex') - let elapsed = process.hrtime.bigint() - start - - return { - triple, - file, - sum, - elapsed, - } + summary.push({ + target, + name, + sum: createHash('sha256').update(content).digest('hex'), + }) } await mkdir(path.resolve(__dirname, '../dist'), { recursive: true }) -// Build platform binaries and checksum them -let results = await Promise.all([ - build('bun-linux-arm64', './tailwindcss-linux-arm64'), - build('bun-linux-x64', './tailwindcss-linux-x64'), - // build('linux-armv7', 'tailwindcss-linux-armv7'), - build('bun-darwin-arm64', './tailwindcss-macos-arm64'), - build('bun-darwin-x64', './tailwindcss-macos-x64'), - // The Windows x64 build uses `bun-baseline` instead of the regular bun build. - // This enables support for running inside the ARM emulation mode. - build('bun-windows-x64-baseline', './tailwindcss-windows-x64.exe'), - // buildForPlatform('win32-arm64', 'tailwindcss-windows-arm64'), -]) - // Write the checksums to a file let sumsFile = path.resolve(__dirname, '../dist/sha256sums.txt') -let sums = results.map(({ file, sum }) => `${sum} ${file}`) - -console.table( - results.map(({ triple, sum, elapsed }) => ({ - triple, - sum, - elapsed: `${(Number(elapsed) / 1e6).toFixed(0)}ms`, - })), -) +let sums = summary.map(({ name, sum }) => `${sum} ./${name}`) await writeFile(sumsFile, sums.join('\n') + '\n') + +console.table(summary.map(({ target, sum }) => ({ target, sum }))) + +let elapsed = process.hrtime.bigint() - start +console.log(`Build completed in ${(Number(elapsed) / 1e6).toFixed(0)}ms`) diff --git a/packages/@tailwindcss-standalone/src/index.ts b/packages/@tailwindcss-standalone/src/index.ts index 98e75c3dc..23a928f8b 100644 --- a/packages/@tailwindcss-standalone/src/index.ts +++ b/packages/@tailwindcss-standalone/src/index.ts @@ -1,4 +1,3 @@ -import fs from 'node:fs' import { createRequire } from 'node:module' import packageJson from 'tailwindcss/package.json' @@ -61,14 +60,39 @@ globalThis.__tw_load = async (id) => { } } globalThis.__tw_version = packageJson.version -globalThis.__tw_readFile = async (path, encoding) => { - // When reading a file from the `$bunfs`, we need to use the synchronous - // `readFileSync` API - let isEmbeddedFileBase = path.includes('/$bunfs/root') || path.includes(':/~BUN/root') - if (!isEmbeddedFileBase) { - return - } - return fs.readFileSync(path, encoding) -} + +// We use a plugin to make sure that the JS APIs are bundled with the standalone +// CLI and can be imported inside configs and plugins +Bun.plugin({ + name: 'bundle-tailwindcss-apis', + target: 'bun', + async setup(build) { + // These imports must be static strings otherwise they won't be bundled + let bundled = { + tailwindcss: await import('tailwindcss'), + 'tailwindcss/colors': await import('tailwindcss/colors'), + 'tailwindcss/colors.js': await import('tailwindcss/colors'), + 'tailwindcss/plugin': await import('tailwindcss/plugin'), + 'tailwindcss/plugin.js': await import('tailwindcss/plugin'), + 'tailwindcss/package.json': await import('tailwindcss/package.json'), + 'tailwindcss/lib/util/flattenColorPalette': + await import('tailwindcss/lib/util/flattenColorPalette'), + 'tailwindcss/lib/util/flattenColorPalette.js': + await import('tailwindcss/lib/util/flattenColorPalette'), + 'tailwindcss/defaultTheme': await import('tailwindcss/defaultTheme'), + 'tailwindcss/defaultTheme.js': await import('tailwindcss/defaultTheme'), + } + + for (let [id, exports] of Object.entries(bundled)) { + build.module(id, () => ({ + loader: 'object', + exports: { + ...exports, + __esModule: true, + }, + })) + } + }, +}) await import('../../@tailwindcss-cli/src/index.ts') diff --git a/packages/@tailwindcss-standalone/src/types.d.ts b/packages/@tailwindcss-standalone/src/types.d.ts index 3212ac5b6..0b5deb6ea 100644 --- a/packages/@tailwindcss-standalone/src/types.d.ts +++ b/packages/@tailwindcss-standalone/src/types.d.ts @@ -6,6 +6,5 @@ declare module '*.css' { declare var __tw_version: string | undefined declare var __tw_resolve: undefined | ((id: string, base?: string) => string | false) declare var __tw_readFile: - | undefined - | ((path: string, encoding: BufferEncoding) => Promise) + undefined | ((path: string, encoding: BufferEncoding) => Promise) declare var __tw_load: undefined | ((path: string) => Promise) diff --git a/packages/@tailwindcss-turbopack/README.md b/packages/@tailwindcss-turbopack/README.md new file mode 100644 index 000000000..ee54b4d03 --- /dev/null +++ b/packages/@tailwindcss-turbopack/README.md @@ -0,0 +1,99 @@ +

+ + + + + Tailwind CSS + + +

+ +

+ A utility-first CSS framework for rapidly building custom user interfaces. +

+ +

+ Build Status + Total Downloads + Latest Release + License +

+ +--- + +## Documentation + +For full documentation, visit [tailwindcss.com](https://tailwindcss.com). + +## Community + +For help, discussion about best practices, or feature ideas: + +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) + +## Contributing + +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. + +--- + +## @tailwindcss/turbopack + +A Turbopack loader for Tailwind CSS v4. + +## Installation + +```sh +npm install @tailwindcss/turbopack +``` + +### Usage + +```javascript +// next.config.js +module.exports = { + turbopack: { + rules: { + '*.css': { + loaders: ['@tailwindcss/turbopack'], + as: '*.css', + }, + }, + }, +} +``` + +Then create a CSS file that imports Tailwind: + +```css +/* src/index.css */ +@import 'tailwindcss'; +``` + +### Options + +#### `base` + +The base directory to scan for class candidates. Defaults to the current working directory. + +```javascript +{ + loader: '@tailwindcss/turbopack', + options: { + base: process.cwd(), + }, +} +``` + +#### `optimize` + +Whether to optimize and minify the output CSS. Defaults to `true` in production mode. + +```javascript +{ + loader: '@tailwindcss/turbopack', + options: { + optimize: true, // or { minify: true } + }, +} +``` diff --git a/packages/@tailwindcss-turbopack/package.json b/packages/@tailwindcss-turbopack/package.json new file mode 100644 index 000000000..abdbbe90e --- /dev/null +++ b/packages/@tailwindcss-turbopack/package.json @@ -0,0 +1,43 @@ +{ + "name": "@tailwindcss/turbopack", + "version": "4.3.3", + "description": "A Turbopack loader for Tailwind CSS v4.", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/tailwindlabs/tailwindcss.git", + "directory": "packages/@tailwindcss-turbopack" + }, + "bugs": "https://github.com/tailwindlabs/tailwindcss/issues", + "homepage": "https://tailwindcss.com", + "scripts": { + "build": "tsup-node", + "dev": "pnpm run build -- --watch" + }, + "files": [ + "dist/" + ], + "publishConfig": { + "provenance": true, + "access": "public" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "workspace:*", + "@tailwindcss/oxide": "workspace:*", + "tailwindcss": "workspace:*" + }, + "devDependencies": { + "@types/node": "catalog:", + "webpack": "catalog:" + } +} diff --git a/packages/@tailwindcss-turbopack/src/index.cts b/packages/@tailwindcss-turbopack/src/index.cts new file mode 100644 index 000000000..dbdded748 --- /dev/null +++ b/packages/@tailwindcss-turbopack/src/index.cts @@ -0,0 +1,5 @@ +import tailwindLoader from './index.ts' + +// CommonJS export for webpack loaders - must be the function directly +// @ts-ignore +export = tailwindLoader diff --git a/packages/@tailwindcss-turbopack/src/index.ts b/packages/@tailwindcss-turbopack/src/index.ts new file mode 100644 index 000000000..a70f28871 --- /dev/null +++ b/packages/@tailwindcss-turbopack/src/index.ts @@ -0,0 +1,287 @@ +import QuickLRU from '@alloc/quick-lru' +import { + compile, + env, + Features, + Instrumentation, + normalizePath, + optimize, + Polyfills, +} from '@tailwindcss/node' +import { clearRequireCache } from '@tailwindcss/node/require-cache' +import { Scanner } from '@tailwindcss/oxide' +import fs from 'node:fs' +import path from 'node:path' +import type { LoaderContext } from 'webpack' + +const DEBUG = env.DEBUG + +export interface LoaderOptions { + /** + * The base directory to scan for class candidates. + * + * Defaults to the current working directory. + */ + base?: string + + /** + * Optimize and minify the output CSS. + */ + optimize?: boolean | { minify?: boolean } +} + +interface CacheEntry { + mtimes: Map + compiler: null | Awaited> + scanner: null | Scanner + candidates: Set + fullRebuildPaths: string[] +} + +const cache = new QuickLRU({ maxSize: 50 }) + +function getCacheKey(resourceId: string, opts: LoaderOptions): string { + return `${resourceId}:${opts.base ?? ''}:${JSON.stringify(opts.optimize)}` +} + +function getContextFromCache(resourceId: string, opts: LoaderOptions): CacheEntry { + let key = getCacheKey(resourceId, opts) + if (cache.has(key)) return cache.get(key)! + let entry: CacheEntry = { + mtimes: new Map(), + compiler: null, + scanner: null, + candidates: new Set(), + fullRebuildPaths: [], + } + cache.set(key, entry) + return entry +} + +export default async function tailwindLoader( + this: LoaderContext, + source: string, +): Promise { + let callback = this.async() + let options = this.getOptions() ?? {} + let inputFile = this.resourcePath + let resourceId = this.resource + let base = options.base ?? process.cwd() + let shouldOptimize = options.optimize ?? process.env.NODE_ENV === 'production' + let isCSSModuleFile = inputFile.endsWith('.module.css') + + using I = new Instrumentation() + + DEBUG && I.start(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + + // Bail out early if this is guaranteed to be a non-Tailwind CSS file. + { + DEBUG && I.start('Quick bail check') + let canBail = !/@(import|reference|theme|variant|config|plugin|apply|tailwind)\b/.test(source) + if (canBail) { + DEBUG && I.end('Quick bail check') + DEBUG && I.end(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + callback(null, source) + return + } + DEBUG && I.end('Quick bail check') + } + + try { + let context = getContextFromCache(resourceId, options) + let inputBasePath = path.dirname(path.resolve(inputFile)) + + // Whether this is the first build or not + let isInitialBuild = context.compiler === null + + async function createCompiler() { + DEBUG && I.start('Setup compiler') + if (context.fullRebuildPaths.length > 0 && !isInitialBuild) { + clearRequireCache(context.fullRebuildPaths) + } + + context.fullRebuildPaths = [] + + DEBUG && I.start('Create compiler') + let compiler = await compile(source, { + from: inputFile, + base: inputBasePath, + shouldRewriteUrls: true, + onDependency: (depPath) => context.fullRebuildPaths.push(depPath), + // In CSS Module files, we have to disable the `@property` polyfill since these will + // emit global `*` rules which are considered to be non-pure and will cause builds + // to fail. + polyfills: isCSSModuleFile ? Polyfills.All ^ Polyfills.AtProperty : Polyfills.All, + }) + DEBUG && I.end('Create compiler') + + DEBUG && I.end('Setup compiler') + return compiler + } + + // Setup the compiler if it doesn't exist yet + context.compiler ??= await createCompiler() + + // Early exit if no Tailwind features are used + if (context.compiler.features === Features.None) { + DEBUG && I.end(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + callback(null, source) + return + } + + let rebuildStrategy: 'full' | 'incremental' = 'incremental' + + // Track file modification times to CSS files + DEBUG && I.start('Register full rebuild paths') + { + // Report dependencies for config files, plugins, etc. + for (let file of context.fullRebuildPaths) { + this.addDependency(path.resolve(file)) + } + + let files = [...context.fullRebuildPaths, inputFile] + + for (let file of files) { + let changedTime: number | null = null + try { + changedTime = fs.statSync(file)?.mtimeMs ?? null + } catch { + // File might not exist + } + + if (changedTime === null) { + if (file === inputFile) { + rebuildStrategy = 'full' + } + continue + } + + let prevTime = context.mtimes.get(file) + if (prevTime === changedTime) continue + + rebuildStrategy = 'full' + context.mtimes.set(file, changedTime) + } + } + DEBUG && I.end('Register full rebuild paths') + + if (rebuildStrategy === 'full' && !isInitialBuild) { + context.compiler = await createCompiler() + } + + let compiler = context.compiler + + // Check if we need to process this file at all + if ( + !( + compiler.features & + (Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities) + ) + ) { + DEBUG && I.end(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + callback(null, source) + return + } + + // Setup or update scanner if needed + if (context.scanner === null || rebuildStrategy === 'full') { + DEBUG && I.start('Setup scanner') + let sources = (() => { + // Disable auto source detection + if (compiler.root === 'none') { + return [] + } + + // No root specified, use the base directory + if (compiler.root === null) { + return [{ base, pattern: '**/*', negated: false }] + } + + // Use the specified root + return [{ ...compiler.root, negated: false }] + })().concat(compiler.sources) + + context.scanner = new Scanner({ sources }) + DEBUG && I.end('Setup scanner') + } + + // Scan for candidates if utilities are used + if (compiler.features & Features.Utilities) { + DEBUG && I.start('Scan for candidates') + for (let candidate of context.scanner.scan()) { + context.candidates.add(candidate) + } + DEBUG && I.end('Scan for candidates') + + DEBUG && I.start('Register dependency messages') + // Add all found files as direct dependencies + let resolvedInputFile = path.resolve(base, inputFile) + for (let file of context.scanner.files) { + let absolutePath = path.resolve(file) + // The CSS file cannot be a dependency of itself + if (absolutePath === resolvedInputFile) { + continue + } + this.addDependency(absolutePath) + } + + // Register context dependencies for glob patterns + for (let glob of context.scanner.globs) { + // Skip negated patterns + if (glob.pattern[0] === '!') continue + + // Avoid adding a dependency on the base directory itself + if (glob.pattern === '*' && base === glob.base) { + continue + } + + this.addContextDependency(path.resolve(glob.base)) + } + + // Validate that source(...) paths are directories + let root = compiler.root + if (root !== 'none' && root !== null) { + let basePath = normalizePath(path.resolve(root.base, root.pattern)) + try { + let stats = fs.statSync(basePath) + if (!stats.isDirectory()) { + throw new Error( + `The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`, + ) + } + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== 'ENOENT') { + throw err + } + // Directory doesn't exist yet, which is fine + } + } + DEBUG && I.end('Register dependency messages') + } + + DEBUG && I.start('Build utilities') + let css = compiler.build([...context.candidates]) + DEBUG && I.end('Build utilities') + + // Optionally optimize the output + let result = css + if (shouldOptimize) { + DEBUG && I.start('Optimization') + let optimized = optimize(css, { + minify: typeof shouldOptimize === 'object' ? shouldOptimize.minify : true, + }) + result = optimized.code + DEBUG && I.end('Optimization') + } + + DEBUG && I.end(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + callback(null, result) + } catch (error) { + // Clear the cache entry on error to force a full rebuild next time + let key = getCacheKey(resourceId, options) + cache.delete(key) + + DEBUG && I.end(`[@tailwindcss/turbopack] ${path.relative(base, inputFile)}`) + callback(error as Error) + } +} diff --git a/packages/@tailwindcss-turbopack/tsconfig.json b/packages/@tailwindcss-turbopack/tsconfig.json new file mode 100644 index 000000000..d2131cbeb --- /dev/null +++ b/packages/@tailwindcss-turbopack/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "verbatimModuleSyntax": false, + }, +} diff --git a/packages/@tailwindcss-turbopack/tsup.config.ts b/packages/@tailwindcss-turbopack/tsup.config.ts new file mode 100644 index 000000000..998eebe4f --- /dev/null +++ b/packages/@tailwindcss-turbopack/tsup.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'tsup' + +export default defineConfig([ + { + format: ['esm'], + clean: true, + minify: true, + cjsInterop: true, + dts: true, + entry: ['src/index.ts'], + }, + { + format: ['cjs'], + minify: true, + cjsInterop: true, + dts: true, + entry: ['src/index.cts'], + }, +]) diff --git a/packages/@tailwindcss-upgrade/README.md b/packages/@tailwindcss-upgrade/README.md index 95ec9d87d..5f532607d 100644 --- a/packages/@tailwindcss-upgrade/README.md +++ b/packages/@tailwindcss-upgrade/README.md @@ -13,10 +13,10 @@

- Build Status + Build Status Total Downloads - Latest Release - License + Latest Release + License

--- @@ -27,14 +27,10 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com). ## Community -For help, discussion about best practices, or any other conversation that would benefit from being searchable: +For help, discussion about best practices, or feature ideas: -[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) - -For chatting with others using the framework: - -[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe) +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) ## Contributing -If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. diff --git a/packages/@tailwindcss-upgrade/package.json b/packages/@tailwindcss-upgrade/package.json index 7183ec3d5..e960d7677 100644 --- a/packages/@tailwindcss-upgrade/package.json +++ b/packages/@tailwindcss-upgrade/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/upgrade", - "version": "4.0.0-beta.7", + "version": "4.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { @@ -27,26 +27,26 @@ "access": "public" }, "dependencies": { - "@tailwindcss/node": "workspace:^", - "@tailwindcss/oxide": "workspace:^", - "braces": "^3.0.3", - "dedent": "1.5.3", - "enhanced-resolve": "^5.17.1", - "globby": "^14.0.2", - "jiti": "^2.0.0-beta.3", + "@tailwindcss/node": "workspace:*", + "@tailwindcss/oxide": "workspace:*", + "dedent": "catalog:", + "enhanced-resolve": "catalog:", + "globby": "^16.2.2", + "jiti": "^2.7.0", "mri": "^1.2.0", "picocolors": "^1.1.1", - "postcss": "^8.4.41", - "postcss-import": "^16.1.0", - "postcss-selector-parser": "^7.0.0", - "prettier": "^3.3.3", - "tree-sitter": "^0.22.1", - "tree-sitter-typescript": "^0.23.2", - "tailwindcss": "workspace:*" + "postcss": "catalog:", + "postcss-import": "^16.1.1", + "postcss-selector-parser": "^7.1.4", + "prettier": "catalog:", + "semver": "^7.8.5", + "tailwindcss": "workspace:*", + "tree-sitter": "^0.25.1", + "tree-sitter-typescript": "^0.23.2" }, "devDependencies": { - "@types/braces": "^3.0.4", "@types/node": "catalog:", - "@types/postcss-import": "^14.0.3" + "@types/postcss-import": "^14.0.3", + "@types/semver": "^7.8.0" } } diff --git a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts b/packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts similarity index 59% rename from packages/@tailwindcss-upgrade/src/migrate-js-config.ts rename to packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts index 6a8a7f401..6a451333e 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/config/migrate-js-config.ts @@ -2,27 +2,31 @@ import { Scanner } from '@tailwindcss/oxide' import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' -import { loadModule } from '../../@tailwindcss-node/src/compile' -import defaultTheme from '../../tailwindcss/dist/default-theme' -import { atRule, toCss, type AstNode } from '../../tailwindcss/src/ast' +import { loadModule } from '../../../../@tailwindcss-node/src/compile' +import defaultTheme from '../../../../tailwindcss/dist/default-theme' +import { atRule, toCss, type AstNode } from '../../../../tailwindcss/src/ast' import { keyPathToCssProperty, themeableValues, -} from '../../tailwindcss/src/compat/apply-config-to-theme' -import { keyframesToRules } from '../../tailwindcss/src/compat/apply-keyframes-to-theme' -import { resolveConfig, type ConfigFile } from '../../tailwindcss/src/compat/config/resolve-config' -import type { ResolvedConfig, ThemeConfig } from '../../tailwindcss/src/compat/config/types' -import { buildCustomContainerUtilityRules } from '../../tailwindcss/src/compat/container' -import { darkModePlugin } from '../../tailwindcss/src/compat/dark-mode' -import type { Config } from '../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../tailwindcss/src/design-system' -import { escape } from '../../tailwindcss/src/utils/escape' +} from '../../../../tailwindcss/src/compat/apply-config-to-theme' +import { keyframesToRules } from '../../../../tailwindcss/src/compat/apply-keyframes-to-theme' +import { + resolveConfig, + type ConfigFile, +} from '../../../../tailwindcss/src/compat/config/resolve-config' +import type { ResolvedConfig, ThemeConfig } from '../../../../tailwindcss/src/compat/config/types' +import { buildCustomContainerUtilityRules } from '../../../../tailwindcss/src/compat/container' +import { darkModePlugin } from '../../../../tailwindcss/src/compat/dark-mode' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { escape } from '../../../../tailwindcss/src/utils/escape' import { isValidOpacityValue, isValidSpacingMultiplier, -} from '../../tailwindcss/src/utils/infer-data-type' -import { findStaticPlugins, type StaticPluginOptions } from './utils/extract-static-plugins' -import { highlight, info, relative } from './utils/renderer' +} from '../../../../tailwindcss/src/utils/infer-data-type' +import * as ValueParser from '../../../../tailwindcss/src/value-parser' +import { findStaticPlugins, type StaticPluginOptions } from '../../utils/extract-static-plugins' +import { highlight, info, relative } from '../../utils/renderer' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) @@ -94,12 +98,14 @@ async function migrateTheme( designSystem: DesignSystem, unresolvedConfig: Config, base: string, -): Promise { +): Promise { // Resolve the config file without applying plugins and presets, as these are // migrated to CSS separately. let configToResolve: ConfigFile = { base, config: { ...unresolvedConfig, plugins: [], presets: undefined }, + reference: false, + src: undefined, } let { resolvedConfig, replacedThemeKeys } = resolveConfig(designSystem, [configToResolve]) @@ -109,10 +115,88 @@ async function migrateTheme( removeUnnecessarySpacingKeys(designSystem, resolvedConfig, replacedThemeKeys) + let css = '' let prevSectionKey = '' - let css = '\n@tw-bucket theme {\n' - css += `\n@theme {\n` - let containsThemeKeys = false + let themeSection: string[] = [] + let keyframesCss = '' + let variants = new Map() + + // Special handling of specific theme keys: + { + if ('keyframes' in resolvedConfig.theme) { + keyframesCss += keyframesToCss(resolvedConfig.theme.keyframes) + delete resolvedConfig.theme.keyframes + } + + if ('container' in resolvedConfig.theme) { + let rules = buildCustomContainerUtilityRules(resolvedConfig.theme.container, designSystem) + if (rules.length > 0) { + // Using `theme` instead of `utility` so it sits before the `@layer + // base` with compatibility CSS. While this is technically a utility, it + // makes a bit more sense to emit this closer to the `@theme` values + // since it is needed for backwards compatibility. + css += `\n@tw-bucket theme {\n` + css += toCss([atRule('@utility', 'container', rules)]) + css += '}\n' // @tw-bucket + } + delete resolvedConfig.theme.container + } + + if ('aria' in resolvedConfig.theme) { + for (let [key, value] of Object.entries(resolvedConfig.theme.aria ?? {})) { + // Will be handled by bare values if the names match. + // E.g.: `aria-foo:flex` should produce `[aria-foo="true"]` + if (new RegExp(`^${key}=(['"]?)true\\1$`).test(`${value}`)) continue + + // Create custom variant + variants.set(`aria-${key}`, `&[aria-${value}]`) + } + delete resolvedConfig.theme.aria + } + + if ('data' in resolvedConfig.theme) { + for (let [key, value] of Object.entries(resolvedConfig.theme.data ?? {})) { + // Will be handled by bare values if the names match. + // E.g.: `data-foo:flex` should produce `[data-foo]` + if (key === value) continue + + // Create custom variant + variants.set(`data-${key}`, `&[data-${value}]`) + } + delete resolvedConfig.theme.data + } + + if ('supports' in resolvedConfig.theme) { + for (let [key, value] of Object.entries(resolvedConfig.theme.supports ?? {})) { + // Will be handled by bare values if the value of the declaration is a + // CSS variable. + let parsed = ValueParser.parse(`${value}`) + + // Unwrap the parens, e.g.: `(foo: var(--bar))` → `foo: var(--bar)` + if (parsed.length === 1 && parsed[0].kind === 'function' && parsed[0].value === '') { + parsed = parsed[0].nodes + } + + // Verify structure: `foo: var(--bar)` + // ^^^ ← must match the `key` + if ( + parsed.length === 3 && + parsed[0].kind === 'word' && + parsed[0].value === key && + parsed[2].kind === 'function' && + parsed[2].value === 'var' + ) { + continue + } + + // Create custom variant + variants.set(`supports-${key}`, `{@supports(${value}){@slot;}}`) + } + delete resolvedConfig.theme.supports + } + } + + // Convert theme values to CSS custom properties for (let [key, value] of themeableValues(resolvedConfig.theme)) { if (typeof value !== 'string' && typeof value !== 'number') { continue @@ -147,56 +231,66 @@ async function migrateTheme( } } - if (key[0] === 'keyframes') { - continue - } - containsThemeKeys = true - let sectionKey = createSectionKey(key) if (sectionKey !== prevSectionKey) { - css += `\n` + themeSection.push('') prevSectionKey = sectionKey } - if (resetNamespaces.has(key[0]) && resetNamespaces.get(key[0]) === false) { - resetNamespaces.set(key[0], true) - let property = keyPathToCssProperty([key[0]]) - if (property !== null) { - css += ` ${escape(`--${property}`)}-*: initial;\n` + let property = keyPathToCssProperty(key) + + if (property !== null) { + if ( + !property.startsWith('default-') && + resetNamespaces.has(key[0]) && + resetNamespaces.get(key[0]) === false + ) { + resetNamespaces.set(key[0], true) + let ns = keyPathToCssProperty([key[0]]) + if (ns !== null) { + themeSection.push(` ${escape(`--${ns}`)}-*: initial;`) + } + } + + themeSection.push(` ${escape(`--${property}`)}: ${value};`) + } + } + + if (keyframesCss) { + themeSection.push('', keyframesCss) + } + + if (themeSection.length > 0) { + css += `\n@tw-bucket theme {\n` + css += `\n@theme {\n` + css += themeSection.join('\n') + '\n' + css += '}\n' // @theme + css += '}\n' // @tw-bucket + } + + if (variants.size > 0) { + css += '\n@tw-bucket custom-variant {\n' + + let previousRoot = '' + for (let [name, selector] of variants) { + let root = name.split('-')[0] + if (previousRoot !== root) css += '\n' + previousRoot = root + + if (selector.startsWith('{')) { + css += `@custom-variant ${name} ${selector}\n` + } else { + css += `@custom-variant ${name} (${selector});\n` } } - - let property = keyPathToCssProperty(key) - if (property !== null) { - css += ` ${escape(`--${property}`)}: ${value};\n` - } + css += '}\n' } - if ('keyframes' in resolvedConfig.theme) { - containsThemeKeys = true - css += '\n' + keyframesToCss(resolvedConfig.theme.keyframes) - } - - if (!containsThemeKeys) { - return null - } - - css += '}\n' // @theme - - if ('container' in resolvedConfig.theme) { - let rules = buildCustomContainerUtilityRules(resolvedConfig.theme.container, designSystem) - if (rules.length > 0) { - css += '\n' + toCss([atRule('@utility', 'container', rules)]) - } - } - - css += '}\n' // @tw-bucket - return css } function migrateDarkMode(unresolvedConfig: Config & { darkMode: any }): string { - let variant: string = '' + let variant: string | string[] = '' let addVariant = (_name: string, _variant: string) => (variant = _variant) let config = () => unresolvedConfig.darkMode darkModePlugin({ config, addVariant }) @@ -204,7 +298,30 @@ function migrateDarkMode(unresolvedConfig: Config & { darkMode: any }): string { if (variant === '') { return '' } - return `\n@tw-bucket variant {\n@variant dark (${variant});\n}\n` + + if (!Array.isArray(variant)) { + variant = [variant] + } + + if (variant.length === 1 && !variant[0].includes('{')) { + return `\n@tw-bucket custom-variant {\n@custom-variant dark (${variant[0]});\n}\n` + } + + let customVariant = '' + for (let variantName of variant) { + // Convert to the block syntax if a block is used + if (variantName.includes('{')) { + customVariant += variantName.replace('}', '{ @slot }}') + '\n' + } else { + customVariant += variantName + '{ @slot }\n' + } + } + + if (customVariant !== '') { + return `\n@tw-bucket custom-variant {\n@custom-variant dark {${customVariant}};\n}\n` + } + + return '' } // Returns a string identifier used to section theme declarations @@ -251,7 +368,11 @@ async function migrateContent( throw new Error('Unsupported content value: ' + pattern) } - let sourceFiles = patternSourceFiles({ base, pattern }) + let sourceFiles = patternSourceFiles({ + base, + pattern: pattern[0] === '!' ? pattern.slice(1) : pattern, + negated: pattern[0] === '!', + }) let autoContentContainsAllSourceFiles = true for (let sourceFile of sourceFiles) { @@ -286,7 +407,7 @@ function canMigrateConfig(unresolvedConfig: Config, source: string): boolean { return false } - // The file may only contain known-migrateable top-level properties + // The file may only contain known-migratable top-level properties let knownProperties = [ 'darkMode', 'content', @@ -295,6 +416,8 @@ function canMigrateConfig(unresolvedConfig: Config, source: string): boolean { 'presets', 'prefix', // Prefix is handled in the dedicated prefix migrator 'corePlugins', + 'future', + 'experimental', ] if (Object.keys(unresolvedConfig).some((key) => !knownProperties.includes(key))) { @@ -309,6 +432,29 @@ function canMigrateConfig(unresolvedConfig: Config, source: string): boolean { return false } + // If there are unknown "future" flags we should bail + if (unresolvedConfig.future && unresolvedConfig.future !== 'all') { + let knownFlags = [ + 'hoverOnlyWhenSupported', + 'respectDefaultRingColorOpacity', + 'disableColorOpacityUtilitiesByDefault', + 'relativeContentPathsByDefault', + ] + + if (Object.keys(unresolvedConfig.future).some((key) => !knownFlags.includes(key))) { + return false + } + } + + // If there are unknown "experimental" flags we should bail + if (unresolvedConfig.experimental && unresolvedConfig.experimental !== 'all') { + let knownFlags = ['generalizedModifiers'] + + if (Object.keys(unresolvedConfig.experimental).some((key) => !knownFlags.includes(key))) { + return false + } + } + // Only migrate the config file if all top-level theme keys are allowed to be // migrated if (theme && typeof theme === 'object') { @@ -325,15 +471,11 @@ const ALLOWED_THEME_KEYS = [ // Used by @tailwindcss/container-queries 'containers', ] -const BLOCKED_THEME_KEYS = ['supports', 'data', 'aria'] function onlyAllowedThemeValues(theme: ThemeConfig): boolean { for (let key of Object.keys(theme)) { if (!ALLOWED_THEME_KEYS.includes(key)) { return false } - if (BLOCKED_THEME_KEYS.includes(key)) { - return false - } } if ('screens' in theme && typeof theme.screens === 'object' && theme.screens !== null) { @@ -352,12 +494,20 @@ function keyframesToCss(keyframes: Record): string { } function autodetectedSourceFiles(base: string) { - let scanner = new Scanner({ sources: [{ base, pattern: '**/*' }] }) + let scanner = new Scanner({ + sources: [ + { + base, + pattern: '**/*', + negated: false, + }, + ], + }) scanner.scan() return scanner.files } -function patternSourceFiles(source: { base: string; pattern: string }): string[] { +function patternSourceFiles(source: { base: string; pattern: string; negated: boolean }): string[] { let scanner = new Scanner({ sources: [source] }) scanner.scan() return scanner.files diff --git a/packages/@tailwindcss-upgrade/src/migrate-postcss.ts b/packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts similarity index 93% rename from packages/@tailwindcss-upgrade/src/migrate-postcss.ts rename to packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts index 2804bae10..e16f52287 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-postcss.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/config/migrate-postcss.ts @@ -1,7 +1,8 @@ import fs from 'node:fs/promises' import path from 'node:path' -import { pkg } from './utils/packages' -import { highlight, info, relative, success, warn } from './utils/renderer' +import { pkg } from '../../utils/packages' +import { highlight, info, relative, success, warn } from '../../utils/renderer' +import { writeFileSafely } from '../../utils/write-file-safely' // Migrates simple PostCSS setups. This is to cover non-dynamic config files // similar to the ones we have all over our docs: @@ -15,6 +16,7 @@ import { highlight, info, relative, success, warn } from './utils/renderer' // autoprefixer: {}, // } // } +// ``` export async function migratePostCSSConfig(base: string) { let ranMigration = false let didMigrate = false @@ -49,7 +51,7 @@ export async function migratePostCSSConfig(base: string) { ranMigration = true if (result) { - await fs.writeFile( + await writeFileSafely( packageJsonPath, JSON.stringify({ ...packageJson, postcss: result?.json }, null, 2), ) @@ -75,7 +77,7 @@ export async function migratePostCSSConfig(base: string) { ranMigration = true if (result) { - await fs.writeFile(jsonConfigPath, JSON.stringify(result.json, null, 2)) + await writeFileSafely(jsonConfigPath, JSON.stringify(result.json, null, 2)) didMigrate = true didAddPostcssClient = result.didAddPostcssClient @@ -102,7 +104,7 @@ export async function migratePostCSSConfig(base: string) { if (location !== null) { try { - await pkg(base).add(['@tailwindcss/postcss@next'], location) + await pkg(base).add(['@tailwindcss/postcss@latest'], location) success(`Installed package: ${highlight('@tailwindcss/postcss')}`, { prefix: '↳ ' }) } catch {} } @@ -135,16 +137,16 @@ async function migratePostCSSJSConfig(configPath: string): Promise<{ didRemovePostCSSImport: boolean } | null> { function isTailwindCSSPlugin(line: string) { - return /['"]?tailwindcss['"]?\: ?\{\}/.test(line) + return /['"]?tailwindcss['"]?: ?\{\}/.test(line) } function isPostCSSImportPlugin(line: string) { - return /['"]?postcss-import['"]?\: ?\{\}/.test(line) + return /['"]?postcss-import['"]?: ?\{\}/.test(line) } function isAutoprefixerPlugin(line: string) { - return /['"]?autoprefixer['"]?\: ?\{\}/.test(line) + return /['"]?autoprefixer['"]?: ?\{\}/.test(line) } function isTailwindCSSNestingPlugin(line: string) { - return /['"]tailwindcss\/nesting['"]\: ?(\{\}|['"]postcss-nesting['"])/.test(line) + return /['"]tailwindcss\/nesting['"]: ?(\{\}|['"]postcss-nesting['"])/.test(line) } info('Migrating PostCSS configuration…') @@ -205,7 +207,7 @@ async function migratePostCSSJSConfig(configPath: string): Promise<{ newLines.push(line) } } - await fs.writeFile(configPath, newLines.join('\n')) + await writeFileSafely(configPath, newLines.join('\n')) return { didAddPostcssClient, didRemoveAutoprefixer, didRemovePostCSSImport } } diff --git a/packages/@tailwindcss-upgrade/src/codemods/css/analyze.ts b/packages/@tailwindcss-upgrade/src/codemods/css/analyze.ts new file mode 100644 index 000000000..c7058df54 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/css/analyze.ts @@ -0,0 +1,303 @@ +import { isGitIgnored } from 'globby' +import path from 'node:path' +import postcss, { type Result } from 'postcss' +import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map' +import { segment } from '../../../../tailwindcss/src/utils/segment' +import { Stylesheet, type StylesheetConnection } from '../../stylesheet' +import { gitRoot } from '../../utils/git' +import { error, highlight, relative } from '../../utils/renderer' +import { resolveCssId } from '../../utils/resolve' + +export async function analyze(stylesheets: Stylesheet[], { base }: { base: string }) { + // Use the project's git root, instead of the `base` to ensure all + // `.gitignore` rules are being used. + let isIgnored = await isGitIgnored({ cwd: gitRoot(base) ?? base }) + let processingQueue: (() => Promise)[] = [] + let stylesheetsByFile = new DefaultMap((file) => { + // We don't want to process ignored files (like node_modules) + try { + if (isIgnored(file)) { + return null + } + } catch { + // If the file is not part of the current working directory (which can + // happen if you import `tailwindcss` and it's loading a shared file from + // pnpm) then this will throw. + return null + } + + try { + let sheet = Stylesheet.loadSync(file) + + // Mutate incoming stylesheets to include the newly discovered sheet + stylesheets.push(sheet) + + // Queue up the processing of this stylesheet + processingQueue.push(() => processor.process(sheet.root, { from: sheet.file! })) + + return sheet + } catch { + return null + } + }) + + // Step 1: Record which `@import` rules point to which stylesheets + // and which stylesheets are parents/children of each other + let processor = postcss([ + { + postcssPlugin: 'mark-import-nodes', + AtRule: { + import(node) { + // Find what the import points to + let id = node.params.match(/['"](.*?)['"]/)?.[1] + if (!id) return + + let basePath = node.source?.input.file + ? path.dirname(node.source.input.file) + : process.cwd() + + // Resolve the import to a file path + let resolvedPath: string | false = false + try { + // We first try to resolve the file as relative to the current file + // to mimic the behavior of `postcss-import` since that's what was + // used to resolve imports in Tailwind CSS v3. + if (id[0] !== '.') { + try { + resolvedPath = resolveCssId(`./${id}`, basePath) + } catch {} + } + + if (!resolvedPath) { + resolvedPath = resolveCssId(id, basePath) + } + } catch (err) { + // Import is a URL, we don't want to process these, but also don't + // want to show an error message for them. + if (id.startsWith('http://') || id.startsWith('https://') || id.startsWith('//')) { + return + } + + // Something went wrong, we can't resolve the import. + error( + `Failed to resolve import: ${highlight(id)} in ${highlight(relative(node.source?.input.file!, basePath))}. Skipping.`, + { prefix: '↳ ' }, + ) + return + } + + if (!resolvedPath) return + + // Find the stylesheet pointing to the resolved path + let stylesheet = stylesheetsByFile.get(resolvedPath) + + // If it _does not_ exist in stylesheets we don't care and skip it + // this is likely because its in node_modules or a workspace package + // that we don't want to modify + if (!stylesheet) return + + // Mark the import node with the ID of the stylesheet it points to + // We will use these later to build lookup tables and modify the AST + node.raws.tailwind_destination_sheet_id = stylesheet.id + + let parent = node.source?.input.file + ? stylesheetsByFile.get(node.source.input.file) + : undefined + + let layers: string[] = [] + + for (let part of segment(node.params, ' ')) { + if (!part.startsWith('layer(')) continue + if (!part.endsWith(')')) continue + + layers.push(part.slice(6, -1).trim()) + } + + // Connect sheets together in a dependency graph + if (parent) { + let meta = { layers } + stylesheet.parents.add({ item: parent, meta }) + parent.children.add({ item: stylesheet, meta }) + } + }, + }, + }, + ]) + + // Seed the map with all the known stylesheets, and queue up the processing of + // each incoming stylesheet. + for (let sheet of stylesheets) { + if (sheet.file) { + stylesheetsByFile.set(sheet.file, sheet) + processingQueue.push(() => processor.process(sheet.root, { from: sheet.file ?? undefined })) + } + } + + // Process all the stylesheets from step 1 + while (processingQueue.length > 0) { + let task = processingQueue.shift()! + await task() + } + + // --- + + let commonPath = process.cwd() + + function pathToString(path: StylesheetConnection[]) { + let parts: string[] = [] + + for (let connection of path) { + if (!connection.item.file) continue + + let filePath = connection.item.file.replace(commonPath, '') + let layers = connection.meta.layers.join(', ') + + if (layers.length > 0) { + parts.push(`${filePath} (layers: ${layers})`) + } else { + parts.push(filePath) + } + } + + return parts.join(' <- ') + } + + let lines: string[] = [] + + for (let sheet of stylesheets) { + if (!sheet.file) continue + + let { convertiblePaths, nonConvertiblePaths } = sheet.analyzeImportPaths() + let isAmbiguous = convertiblePaths.length > 0 && nonConvertiblePaths.length > 0 + + if (!isAmbiguous) continue + + sheet.canMigrate = false + + let filePath = sheet.file.replace(commonPath, '') + + for (let path of convertiblePaths) { + lines.push(`- ${filePath} <- ${pathToString(path)}`) + } + + for (let path of nonConvertiblePaths) { + lines.push(`- ${filePath} <- ${pathToString(path)}`) + } + } + + if (lines.length === 0) { + let tailwindRootLeafs = new Set() + + for (let sheet of stylesheets) { + // If the current file already contains `@config`, then we can assume it's + // a Tailwind CSS root file. + sheet.root.walkAtRules('config', () => { + sheet.isTailwindRoot = true + return false + }) + if (sheet.isTailwindRoot) continue + + // If an `@tailwind` at-rule, or `@import "tailwindcss"` is present, + // then we can assume it's a file where Tailwind CSS might be configured. + // + // However, if 2 or more stylesheets exist with these rules that share a + // common parent, then we want to mark the common parent as the root + // stylesheet instead. + sheet.root.walkAtRules((node) => { + if ( + node.name === 'tailwind' || + (node.name === 'import' && node.params.match(/^["']tailwindcss["']/)) || + (node.name === 'import' && node.params.match(/^["']tailwindcss\/.*?["']$/)) + ) { + sheet.isTailwindRoot = true + tailwindRootLeafs.add(sheet) + } + }) + } + + // Only a single Tailwind CSS root file exists, no need to do anything else. + if (tailwindRootLeafs.size <= 1) { + return + } + + // Mark the common parent as the root file + { + // Group each sheet from tailwindRootLeafs by their common parent + let commonParents = new DefaultMap>(() => new Set()) + + // Seed common parents with leafs + for (let sheet of tailwindRootLeafs) { + commonParents.get(sheet).add(sheet) + } + + // If any 2 common parents come from the same tree, then all children of + // parent A and parent B will be moved to the parent of parent A and + // parent B. Parent A and parent B will be removed. + let repeat = true + repeat: while (repeat) { + repeat = false + + for (let [sheetA, childrenA] of commonParents) { + for (let [sheetB, childrenB] of commonParents) { + if (sheetA === sheetB) continue + + // Ancestors from self to root. Reversed order so we find the + // nearest common parent first + // + // Including self because if you compare a sheet with its parent, + // then the parent is still the common sheet between the two. In + // this case, the parent is the root file. + let ancestorsA = [sheetA].concat(Array.from(sheetA.ancestors()).reverse()) + let ancestorsB = [sheetB].concat(Array.from(sheetB.ancestors()).reverse()) + + for (let parentA of ancestorsA) { + for (let parentB of ancestorsB) { + if (parentA !== parentB) continue + + // Found the parent + let parent = parentA + + commonParents.delete(sheetA) + commonParents.delete(sheetB) + + for (let child of childrenA) { + commonParents.get(parent).add(child) + } + + for (let child of childrenB) { + commonParents.get(parent).add(child) + } + + // Found a common parent between sheet A and sheet B. We can + // stop looking for more common parents between A and B, and + // continue with the next sheet. + repeat = true + continue repeat + } + } + } + } + } + + // Mark the common parent as the Tailwind CSS root file, and remove the + // flag from each leaf. + for (let [parent, children] of commonParents) { + parent.isTailwindRoot = true + + for (let child of children) { + if (parent === child) continue + + child.isTailwindRoot = false + } + } + return + } + } + + { + let error = `You have one or more stylesheets that are imported into a utility layer and non-utility layer.\n` + error += `We cannot convert stylesheets under these conditions. Please look at the following stylesheets:\n` + + throw new Error(error + lines.join('\n')) + } +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/fixtures/test.css b/packages/@tailwindcss-upgrade/src/codemods/css/fixtures/test.css similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/fixtures/test.css rename to packages/@tailwindcss-upgrade/src/codemods/css/fixtures/test.css diff --git a/packages/@tailwindcss-upgrade/src/codemods/format-nodes.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/format-nodes.test.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/format-nodes.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/format-nodes.test.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts b/packages/@tailwindcss-upgrade/src/codemods/css/format-nodes.ts similarity index 98% rename from packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/format-nodes.ts index a5c980505..489f2d349 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/format-nodes.ts @@ -1,6 +1,6 @@ import postcss, { type ChildNode, type Plugin, type Root } from 'postcss' import { format, type Options } from 'prettier' -import { walk } from '../utils/walk' +import { walk } from '../../utils/walk' const FORMAT_OPTIONS: Options = { parser: 'css', diff --git a/packages/@tailwindcss-upgrade/src/codemods/css/link.ts b/packages/@tailwindcss-upgrade/src/codemods/css/link.ts new file mode 100644 index 000000000..04c2dd770 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/css/link.ts @@ -0,0 +1,122 @@ +import { normalizePath } from '@tailwindcss/node' +import path from 'node:path' +import postcss from 'postcss' +import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map' +import { Stylesheet } from '../../stylesheet' +import { error, highlight, relative, success } from '../../utils/renderer' +import { detectConfigPath } from '../template/prepare-config' + +export async function linkConfigs( + stylesheets: Stylesheet[], + { configPath, base }: { configPath: string | null; base: string }, +) { + let rootStylesheets = stylesheets.filter((sheet) => sheet.isTailwindRoot) + if (rootStylesheets.length === 0) { + throw new Error( + `Cannot find any CSS files that reference Tailwind CSS.\nBefore your project can be upgraded you need to create a CSS file that imports Tailwind CSS or uses ${highlight('@tailwind')}.`, + ) + } + let withoutAtConfig = rootStylesheets.filter((sheet) => { + let hasConfig = false + sheet.root.walkAtRules('config', (node) => { + let configPath = path.resolve(path.dirname(sheet.file!), node.params.slice(1, -1)) + sheet.linkedConfigPath = configPath + hasConfig = true + return false + }) + return !hasConfig + }) + + // All stylesheets have a `@config` directives + if (withoutAtConfig.length === 0) return + + // Find the config file path for each stylesheet + let configPathBySheet = new Map() + let sheetByConfigPath = new DefaultMap>(() => new Set()) + for (let sheet of withoutAtConfig) { + if (!sheet.file) continue + + let localConfigPath = configPath as string + if (configPath === null) { + localConfigPath = await detectConfigPath(path.dirname(sheet.file), base) + } else if (!path.isAbsolute(localConfigPath)) { + localConfigPath = path.resolve(base, localConfigPath) + } + + configPathBySheet.set(sheet, localConfigPath) + sheetByConfigPath.get(localConfigPath).add(sheet) + } + + let problematicStylesheets = new Set() + for (let sheets of sheetByConfigPath.values()) { + if (sheets.size > 1) { + for (let sheet of sheets) { + problematicStylesheets.add(sheet) + } + } + } + + // There are multiple "root" files without `@config` directives. Manual + // intervention is needed to link to the correct Tailwind config files. + if (problematicStylesheets.size > 1) { + for (let sheet of problematicStylesheets) { + error( + `Could not determine configuration file for: ${highlight(relative(sheet.file!, base))}\nUpdate your stylesheet to use ${highlight('@config')} to specify the correct configuration file explicitly and then run the upgrade tool again.`, + { prefix: '↳ ' }, + ) + } + + process.exit(1) + } + + let relativePath = relative + for (let [sheet, configPath] of configPathBySheet) { + try { + if (!sheet || !sheet.file) return + success( + `Linked ${highlight(relativePath(configPath, base))} to ${highlight(relativePath(sheet.file, base))}`, + { prefix: '↳ ' }, + ) + + // Link the `@config` directive to the root stylesheets + + // Track the config file path on the stylesheet itself for easy access + // without traversing the CSS ast and finding the corresponding + // `@config` later. + sheet.linkedConfigPath = configPath + + // Create a relative path from the current file to the config file. + let relative = path.relative(path.dirname(sheet.file), configPath) + + // If the path points to a file in the same directory, `path.relative` will + // remove the leading `./` and we need to add it back in order to still + // consider the path relative + if (!relative.startsWith('.') && !path.isAbsolute(relative)) { + relative = './' + relative + } + + relative = normalizePath(relative) + + // Add the `@config` directive to the root stylesheet. + { + let target = sheet.root as postcss.Root | postcss.AtRule + let atConfig = postcss.atRule({ name: 'config', params: `'${relative}'` }) + + sheet.root.walkAtRules((node) => { + if (node.name === 'tailwind' || node.name === 'import') { + target = node + } + }) + + if (target.type === 'root') { + sheet.root.prepend(atConfig) + } else if (target.type === 'atrule') { + target.after(atConfig) + } + } + } catch (e: any) { + error('Could not load the configuration file: ' + e.message, { prefix: '↳ ' }) + process.exit(1) + } + } +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.test.ts similarity index 53% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.test.ts index 362f52054..9f6a92008 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.test.ts @@ -1,9 +1,11 @@ import { __unstable__loadDesignSystem } from '@tailwindcss/node' import dedent from 'dedent' import postcss from 'postcss' -import { expect, it } from 'vitest' -import type { Config } from '../../../tailwindcss/src/compat/plugin-api' +import { expect, it, vi } from 'vitest' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import * as versions from '../../utils/version' import { migrateAtApply } from './migrate-at-apply' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) const css = dedent @@ -11,6 +13,11 @@ async function migrate(input: string, config: Config = {}) { let designSystem = await __unstable__loadDesignSystem( css` @import 'tailwindcss'; + + /* TODO(perf): Only here to speed up the tests */ + @theme { + --*: initial; + } `, { base: __dirname }, ) @@ -54,21 +61,6 @@ it('should append `!` to each utility, when using `!important`', async () => { `) }) -// TODO: Handle SCSS syntax -it.skip('should append `!` to each utility, when using `#{!important}`', async () => { - expect( - await migrate(css` - .foo { - @apply flex flex-col #{!important}; - } - `), - ).toMatchInlineSnapshot(` - ".foo { - @apply flex! flex-col!; - }" - `) -}) - it('should move the legacy `!` prefix, to the new `!` postfix notation', async () => { expect( await migrate(css` @@ -83,35 +75,44 @@ it('should move the legacy `!` prefix, to the new `!` postfix notation', async ( `) }) -it('should apply all candidate migration when migrating with a config', async () => { - async function migrateWithPrefix(input: string) { - return postcss() - .use( - migrateAtApply({ - designSystem: await __unstable__loadDesignSystem( - css` - @import 'tailwindcss' prefix(tw); - `, - { base: __dirname }, - ), - userConfig: { - prefix: 'tw_', - }, - }), - ) - .process(input, { from: expect.getState().testPath }) - .then((result) => result.css) - } +it( + 'should apply all candidate migration when migrating with a config', + { timeout: 10_000 }, + async () => { + async function migrateWithPrefix(input: string) { + return postcss() + .use( + migrateAtApply({ + designSystem: await __unstable__loadDesignSystem( + css` + @import 'tailwindcss' prefix(tw); - expect( - await migrateWithPrefix(css` - .foo { - @apply !tw_flex [color:--my-color] tw_bg-gradient-to-t; - } - `), - ).toMatchInlineSnapshot(` + /* TODO(perf): Only here to speed up the tests */ + @theme { + --*: initial; + } + `, + { base: __dirname }, + ), + userConfig: { + prefix: 'tw_', + }, + }), + ) + .process(input, { from: expect.getState().testPath }) + .then((result) => result.css) + } + + expect( + await migrateWithPrefix(css` + .foo { + @apply !tw_flex [color:--my-color] tw_bg-gradient-to-t; + } + `), + ).toMatchInlineSnapshot(` ".foo { - @apply tw:flex! tw:[color:var(--my-color)] tw:bg-linear-to-t; + @apply tw:flex! tw:text-(--my-color) tw:bg-linear-to-t; }" `) -}) + }, +) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.ts similarity index 83% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.ts index 0d3a1f035..69f92688d 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-apply.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-apply.ts @@ -1,15 +1,15 @@ import type { AtRule, Plugin } from 'postcss' -import type { Config } from '../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import { segment } from '../../../tailwindcss/src/utils/segment' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { segment } from '../../../../tailwindcss/src/utils/segment' import { migrateCandidate } from '../template/migrate' export function migrateAtApply({ designSystem, userConfig, }: { - designSystem: DesignSystem - userConfig: Config + designSystem: DesignSystem | null + userConfig: Config | null }): Plugin { function migrate(atRule: AtRule) { let utilities = atRule.params.split(/(\s+)/) @@ -35,6 +35,8 @@ export function migrateAtApply({ }) return async () => { + if (!designSystem) return + // If we have a valid designSystem and config setup, we can run all // candidate migrations on each utility params = await Promise.all( diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.test.ts similarity index 88% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.test.ts index 17aeb8577..e115951d3 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.test.ts @@ -1,10 +1,12 @@ import dedent from 'dedent' import postcss from 'postcss' -import { describe, expect, it } from 'vitest' -import { Stylesheet } from '../stylesheet' +import { describe, expect, it, vi } from 'vitest' +import { Stylesheet } from '../../stylesheet' +import * as versions from '../../utils/version' import { formatNodes } from './format-nodes' import { migrateAtLayerUtilities } from './migrate-at-layer-utilities' import { sortBuckets } from './sort-buckets' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) const css = dedent @@ -37,7 +39,7 @@ async function migrate( .use(sortBuckets()) .use(formatNodes()) .process(stylesheet.root!, { from: expect.getState().testPath }) - .then((result) => result.css) + .then((result) => `\n${result.css.trim()}\n`) } it('should migrate simple `@layer utilities` to `@utility`', async () => { @@ -50,9 +52,11 @@ it('should migrate simple `@layer utilities` to `@utility`', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; - }" + } + " `) }) @@ -67,13 +71,15 @@ it('should split multiple selectors in separate utilities', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; } @utility bar { color: red; - }" + } + " `) }) @@ -97,14 +103,16 @@ it('should merge `@utility` with the same name', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; font-weight: bold; } .bar { color: blue; - }" + } + " `) }) @@ -147,7 +155,8 @@ it('should leave non-class utilities alone', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { /* 2. */ /* 2.1. */ color: red; @@ -180,7 +189,58 @@ it('should leave non-class utilities alone', async () => { font-weight: bold; } } - }" + } + " + `) +}) + +it('should not convert empty classes into `@utility` since that would make them invalid', async () => { + expect( + await migrate(css` + @layer utilities { + .good { + color: red; + } + + .bad-empty { + } + + /* Comment above */ + .bad-with-comment-above { + } + + .bad-with-comment-inside { + /* Comment inside */ + } + + .bad-with-comment-below { + } + /* Comment below */ + } + `), + ).toMatchInlineSnapshot(` + " + @utility good { + color: red; + } + + @layer utilities { + .bad-empty { + } + + /* Comment above */ + .bad-with-comment-above { + } + + .bad-with-comment-inside { + /* Comment inside */ + } + + .bad-with-comment-below { + } + /* Comment below */ + } + " `) }) @@ -202,7 +262,8 @@ it('should migrate simple `@layer utilities` with nesting to `@utility`', async } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; &:hover { @@ -212,7 +273,8 @@ it('should migrate simple `@layer utilities` with nesting to `@utility`', async &:focus { color: green; } - }" + } + " `) }) @@ -230,13 +292,15 @@ it('should migrate multiple simple `@layer utilities` to `@utility`', async () = } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; } @utility bar { color: blue; - }" + } + " `) }) @@ -258,7 +322,8 @@ it('should not migrate Rules inside of Rules to a `@utility`', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { color: red; } @@ -268,7 +333,8 @@ it('should not migrate Rules inside of Rules to a `@utility`', async () => { .baz { color: green; } - }" + } + " `) }) @@ -284,11 +350,13 @@ it('should invert at-rules to make them migrate-able', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { @media (min-width: 640px) { color: red; } - }" + } + " `) }) @@ -312,7 +380,8 @@ it('should migrate at-rules with multiple utilities and invert them', async () = } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { @media (min-width: 640px) { color: red; } @@ -322,7 +391,8 @@ it('should migrate at-rules with multiple utilities and invert them', async () = @media (min-width: 640px) { color: blue; } - }" + } + " `) }) @@ -354,7 +424,8 @@ it('should migrate deeply nested at-rules with multiple utilities and invert the } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { @media (min-width: 640px) { color: red; } @@ -382,7 +453,8 @@ it('should migrate deeply nested at-rules with multiple utilities and invert the } } } - }" + } + " `) }) @@ -396,11 +468,13 @@ it('should migrate classes with pseudo elements', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { &::-webkit-scrollbar { display: none; } - }" + } + " `) }) @@ -414,11 +488,13 @@ it('should migrate classes with attribute selectors', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { &[data-checked=''] { display: none; } - }" + } + " `) }) @@ -432,11 +508,13 @@ it('should migrate classes with element selectors', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { & main { display: none; } - }" + } + " `) }) @@ -450,11 +528,13 @@ it('should migrate classes attached to an element selector', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { &main { display: none; } - }" + } + " `) }) @@ -468,11 +548,13 @@ it('should migrate classes with id selectors', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { &#main { display: none; } - }" + } + " `) }) @@ -486,7 +568,8 @@ it('should migrate classes with another attached class', async () => { } `), ).toMatchInlineSnapshot(` - "@utility no-scrollbar { + " + @utility no-scrollbar { &.main { display: none; } @@ -496,7 +579,8 @@ it('should migrate classes with another attached class', async () => { &.no-scrollbar { display: none; } - }" + } + " `) }) @@ -510,7 +594,8 @@ it('should migrate a selector with multiple classes to multiple @utility definit } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { & .bar:hover .baz:focus { display: none; } @@ -526,7 +611,8 @@ it('should migrate a selector with multiple classes to multiple @utility definit .foo .bar:hover &:focus { display: none; } - }" + } + " `) }) @@ -546,7 +632,8 @@ it('should merge `@utility` definitions with the same name', async () => { } `), ).toMatchInlineSnapshot(` - "@utility step { + " + @utility step { counter-increment: step; &:before { @@ -554,7 +641,8 @@ it('should merge `@utility` definitions with the same name', async () => { @apply ml-[-41px]; content: counter(step); } - }" + } + " `) }) @@ -568,7 +656,8 @@ it('should not migrate nested classes inside a `:not(…)`', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { & .bar:not(.qux):has(.baz) { display: none; } @@ -584,7 +673,8 @@ it('should not migrate nested classes inside a `:not(…)`', async () => { .foo .bar:not(.qux):has(&) { display: none; } - }" + } + " `) }) @@ -618,7 +708,8 @@ it('should migrate advanced combinations', async () => { } `), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { @media (width >= 100px) { @supports (display: none) { & .bar:not(.qux):has(.baz) { @@ -657,7 +748,8 @@ it('should migrate advanced combinations', async () => { } } } - }" + } + " `) }) @@ -675,12 +767,15 @@ describe('comments', () => { } `), ).toMatchInlineSnapshot(` - "/* Start of utilities: */ + " + /* Start of utilities: */ + @utility foo { /* Utility #1 */ /* Declarations: */ color: red; - }" + } + " `) }) @@ -697,7 +792,9 @@ describe('comments', () => { } `), ).toMatchInlineSnapshot(` - "/* Start of utilities: */ + " + /* Start of utilities: */ + @utility foo { /* Foo & Bar */ & .bar { @@ -705,13 +802,15 @@ describe('comments', () => { color: red; } } + @utility bar { /* Foo & Bar */ .foo & { /* Declarations: */ color: red; } - }" + } + " `) }) @@ -731,7 +830,9 @@ describe('comments', () => { } `), ).toMatchInlineSnapshot(` - "/* Start of utilities: */ + " + /* Start of utilities: */ + @utility foo { /* Mobile only */ @media (width <= 640px) { @@ -739,7 +840,8 @@ describe('comments', () => { /* Declarations: */ color: red; } - }" + } + " `) }) @@ -778,7 +880,9 @@ describe('comments', () => { /* After */ `), ).toMatchInlineSnapshot(` - "/* Tailwind Utilities: */ + " + /* Tailwind Utilities: */ + @utility no-scrollbar { /* Chrome, Safari and Opera */ /* Second comment */ @@ -805,7 +909,8 @@ describe('comments', () => { .after { /* Inside */ } - /* After */" + /* After */ + " `) }) }) @@ -830,7 +935,8 @@ it('should not lose attribute selectors', async () => { } `), ).toMatchInlineSnapshot(` - "@layer components { + " + @layer components { #TableOfContents { .toc a { @apply block max-w-full truncate py-1 pl-2 hover:font-medium hover:no-underline; @@ -843,7 +949,8 @@ it('should not lose attribute selectors', async () => { } } } - }" + } + " `) }) @@ -861,11 +968,13 @@ describe('layered stylesheets', () => { layers: ['utilities'], }), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { /* Utility #1 */ /* Declarations: */ color: red; - }" + } + " `) }) @@ -882,11 +991,13 @@ describe('layered stylesheets', () => { layers: ['components'], }), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { /* Utility #1 */ /* Declarations: */ color: red; - }" + } + " `) }) @@ -903,11 +1014,13 @@ describe('layered stylesheets', () => { layers: ['foo'], }), ).toMatchInlineSnapshot(` - "/* Utility #1 */ + " + /* Utility #1 */ .foo { /* Declarations: */ color: red; - }" + } + " `) }) @@ -927,7 +1040,8 @@ describe('layered stylesheets', () => { layers: ['utilities'], }), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { /* Utility #1 */ /* Declarations: */ color: red; @@ -935,7 +1049,8 @@ describe('layered stylesheets', () => { #main { color: red; - }" + } + " `) }) @@ -976,7 +1091,8 @@ describe('layered stylesheets', () => { layers: ['utilities'], }), ).toMatchInlineSnapshot(` - "@utility foo { + " + @utility foo { @layer utilities { @layer utilities { /* Utility #1 */ @@ -1009,7 +1125,8 @@ describe('layered stylesheets', () => { #secondary { color: red; - }" + } + " `) }) @@ -1026,11 +1143,13 @@ describe('layered stylesheets', () => { layers: ['utilities'], }), ).toMatchInlineSnapshot(` - "@import 'thing'; + " + @import 'thing'; @utility foo { color: red; - }" + } + " `) }) @@ -1047,11 +1166,13 @@ describe('layered stylesheets', () => { layers: ['utilities'], }), ).toMatchInlineSnapshot(` - "@charset "utf-8"; + " + @charset "utf-8"; @utility foo { color: red; - }" + } + " `) }) }) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.ts similarity index 83% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.ts index f0f11d3d6..6bcb72d47 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-at-layer-utilities.ts @@ -1,11 +1,17 @@ -import { type AtRule, type Comment, type Plugin, type Rule } from 'postcss' +import { type AtRule, type Comment, type Container, type Plugin, type Rule } from 'postcss' import SelectorParser from 'postcss-selector-parser' -import { segment } from '../../../tailwindcss/src/utils/segment' -import { Stylesheet } from '../stylesheet' -import { walk, WalkAction, walkDepth } from '../utils/walk' +import { segment } from '../../../../tailwindcss/src/utils/segment' +import { Stylesheet } from '../../stylesheet' +import * as version from '../../utils/version' +import { walk, WalkAction, walkDepth } from '../../utils/walk' export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { function migrate(atRule: AtRule) { + // Migrating `@layer utilities` to `@utility` is only supported in Tailwind + // CSS v3 projects. Tailwind CSS v4 projects could also have `@layer + // utilities` but those aren't actual utilities. + if (!version.isMajor(3)) return + // Only migrate `@layer utilities` and `@layer components`. if (atRule.params !== 'utilities' && atRule.params !== 'components') return @@ -37,6 +43,7 @@ export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { walk(atRule, (node) => { if (node.type !== 'rule') return + if (isEmpty(node)) return // Find all the classes in the selector SelectorParser((selectors) => { @@ -178,6 +185,18 @@ export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { toRemove.push(node.parent?.nodes.at(i) as Comment) } } + + let commentsAfter: Comment[] = [] + for (let i = idx + 1; i < (node.parent?.nodes.length ?? 0); i++) { + if (node.parent?.nodes.at(i)?.type === 'rule') { + commentsAfter = [] + break + } + if (node.parent?.nodes.at(i)?.type === 'comment') { + commentsAfter.push(node.parent?.nodes.at(i) as Comment) + } + } + toRemove.push(...commentsAfter) } for (let node of toRemove) { node.remove() @@ -225,8 +244,12 @@ export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { } } - // Remove empty rules - if ((node.type === 'rule' || node.type === 'atrule') && node.nodes?.length === 0) { + // Remove empty rules from `@utility` clones and empty wrapper at-rules + // from the default `@layer` clone. + if ( + (node.type === 'atrule' && node.nodes?.length === 0) || + (clone !== defaultsAtRule && node.type === 'rule' && node.nodes?.length === 0) + ) { node.remove() } @@ -255,6 +278,11 @@ export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { // Remove empty clones entirely if (clone.nodes?.length === 0) { clones.splice(idx, 1) + } else if (clone === defaultsAtRule) { + let first = clone.nodes?.[0] + if (first) { + first.raws.before = first.raws.before?.replace(/\n\s*\n/g, '\n') + } } } @@ -310,3 +338,17 @@ export function migrateAtLayerUtilities(stylesheet: Stylesheet): Plugin { }, } } + +function isEmpty(node: Container): boolean { + // We have no body + if (node.nodes === undefined) return false + + // We have a body, but no nodes + if (node.nodes.length === 0) return true + + // We have nodes, but they are only comments which we consider empty + if (node.nodes.every((child) => child.type === 'comment')) return true + + // Not empty + return false +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-config.ts similarity index 90% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-config.ts index b54fec34c..2004d0969 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-config.ts @@ -1,8 +1,8 @@ import path from 'node:path' import postcss, { AtRule, type Plugin } from 'postcss' -import { normalizePath } from '../../../@tailwindcss-node/src/normalize-path' -import type { JSConfigMigration } from '../migrate-js-config' -import type { Stylesheet } from '../stylesheet' +import { normalizePath } from '../../../../@tailwindcss-node/src/normalize-path' +import type { Stylesheet } from '../../stylesheet' +import type { JSConfigMigration } from '../config/migrate-js-config' const ALREADY_INJECTED = new WeakMap() @@ -11,10 +11,11 @@ export function migrateConfig( { configFilePath, jsConfigMigration, - }: { configFilePath: string; jsConfigMigration: JSConfigMigration }, + }: { configFilePath: string | null; jsConfigMigration: JSConfigMigration | null }, ): Plugin { function migrate() { if (!sheet.isTailwindRoot) return + if (!configFilePath) return let alreadyInjected = ALREADY_INJECTED.get(sheet) if (alreadyInjected && alreadyInjected.includes(configFilePath)) { diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-import.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-import.test.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-import.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-import.test.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-import.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-import.ts similarity index 85% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-import.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-import.ts index cfdf00ee6..419f5aa32 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-import.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-import.ts @@ -1,9 +1,9 @@ import fs from 'node:fs/promises' import { dirname, resolve } from 'node:path' import { type Plugin, type Root } from 'postcss' -import { parseImportParams } from '../../../tailwindcss/src/at-import' -import { segment } from '../../../tailwindcss/src/utils/segment' -import * as ValueParser from '../../../tailwindcss/src/value-parser' +import { parseImportParams } from '../../../../tailwindcss/src/at-import' +import { segment } from '../../../../tailwindcss/src/utils/segment' +import * as ValueParser from '../../../../tailwindcss/src/value-parser' export function migrateImport(): Plugin { async function migrate(root: Root) { @@ -16,6 +16,7 @@ export function migrateImport(): Plugin { let [firstParam, ...rest] = segment(rule.params, ' ') let params = parseImportParams(ValueParser.parse(firstParam)) + if (!params) return let isRelative = params.uri[0] === '.' let hasCssExtension = params.uri.endsWith('.css') diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.test.ts similarity index 97% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.test.ts index f4afd15fa..18ce72e8b 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.test.ts @@ -2,7 +2,7 @@ import { __unstable__loadDesignSystem } from '@tailwindcss/node' import dedent from 'dedent' import postcss from 'postcss' import { expect, it } from 'vitest' -import type { UserConfig } from '../../../tailwindcss/src/compat/config/types' +import type { UserConfig } from '../../../../tailwindcss/src/compat/config/types' import { formatNodes } from './format-nodes' import { migrateMediaScreen } from './migrate-media-screen' import { sortBuckets } from './sort-buckets' diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.ts similarity index 64% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.ts index 40fe29c3c..c10e9ee99 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-media-screen.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-media-screen.ts @@ -1,25 +1,27 @@ import { type Plugin, type Root } from 'postcss' -import { resolveConfig } from '../../../tailwindcss/src/compat/config/resolve-config' -import type { Config } from '../../../tailwindcss/src/compat/plugin-api' -import { buildMediaQuery } from '../../../tailwindcss/src/compat/screens-config' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import { DefaultMap } from '../../../tailwindcss/src/utils/default-map' +import { resolveConfig } from '../../../../tailwindcss/src/compat/config/resolve-config' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import { buildMediaQuery } from '../../../../tailwindcss/src/compat/screens-config' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map' export function migrateMediaScreen({ designSystem, userConfig, }: { - designSystem?: DesignSystem - userConfig?: Config + designSystem?: DesignSystem | null + userConfig?: Config | null } = {}): Plugin { function migrate(root: Root) { if (!designSystem || !userConfig) return - let { resolvedConfig } = resolveConfig(designSystem, [{ base: '', config: userConfig }]) + let { resolvedConfig } = resolveConfig(designSystem, [ + { base: '', config: userConfig, reference: false, src: undefined }, + ]) let screens = resolvedConfig?.theme?.screens || {} let mediaQueries = new DefaultMap((name) => { - let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`) ?? screens?.[name] + let value = designSystem?.resolveThemeValue(`--breakpoint-${name}`, true) ?? screens?.[name] if (typeof value === 'string') return `(width >= theme(--breakpoint-${name}))` return value ? buildMediaQuery(value) : null }) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-missing-layers.test.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-missing-layers.test.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-missing-layers.ts similarity index 97% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-missing-layers.ts index 7dad26e73..10c9d55de 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-missing-layers.ts @@ -1,5 +1,5 @@ import { AtRule, type ChildNode, type Plugin, type Root } from 'postcss' -import { segment } from '../../../tailwindcss/src/utils/segment' +import { segment } from '../../../../tailwindcss/src/utils/segment' export function migrateMissingLayers(): Plugin { function migrate(root: Root) { @@ -16,6 +16,7 @@ export function migrateMissingLayers(): Plugin { node.name === 'source' || node.name === 'theme' || node.name === 'utility' || + node.name === 'custom-variant' || node.name === 'variant' ) { if (bucket.length > 0) { diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.test.ts similarity index 85% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.test.ts index 21793e1ed..2d8b70041 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.test.ts @@ -1,10 +1,12 @@ import { __unstable__loadDesignSystem } from '@tailwindcss/node' import dedent from 'dedent' import postcss from 'postcss' -import { expect, it } from 'vitest' +import { expect, it, vi } from 'vitest' +import * as versions from '../../utils/version' import { formatNodes } from './format-nodes' import { migratePreflight } from './migrate-preflight' import { sortBuckets } from './sort-buckets' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) const css = dedent @@ -33,7 +35,7 @@ it("should add compatibility CSS after the `@import 'tailwindcss'`", async () => "@import 'tailwindcss'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -46,7 +48,7 @@ it("should add compatibility CSS after the `@import 'tailwindcss'`", async () => ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } }" `) @@ -65,7 +67,7 @@ it('should add the compatibility CSS after the last `@import`', async () => { @import './bar.css'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -78,7 +80,7 @@ it('should add the compatibility CSS after the last `@import`', async () => { ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } }" `) @@ -111,7 +113,7 @@ it('should add the compatibility CSS after the last import, even if a body-less @import './bar.css'; /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -124,7 +126,7 @@ it('should add the compatibility CSS after the last import, even if a body-less ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } }" `) @@ -135,7 +137,7 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai await migrate(css` @import 'tailwindcss'; - @variant foo { + @custom-variant foo { } @utility bar { @@ -153,11 +155,11 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai ).toMatchInlineSnapshot(` "@import 'tailwindcss'; - @variant foo { + @custom-variant foo { } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -170,7 +172,7 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } @@ -193,7 +195,7 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai await migrate(css` @import 'tailwindcss/preflight'; - @variant foo { + @custom-variant foo { } @utility bar { @@ -211,11 +213,11 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai ).toMatchInlineSnapshot(` "@import 'tailwindcss/preflight'; - @variant foo { + @custom-variant foo { } /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -228,7 +230,7 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai ::before, ::backdrop, ::file-selector-button { - border-color: var(--color-gray-200, currentColor); + border-color: var(--color-gray-200, currentcolor); } } @@ -249,7 +251,7 @@ it('should add the compatibility CSS before the first `@layer base` (if the "tai it('should not add the backwards compatibility CSS when no `@import "tailwindcss"` or `@import "tailwindcss/preflight"` exists', async () => { expect( await migrate(css` - @variant foo { + @custom-variant foo { } @utility bar { @@ -265,7 +267,7 @@ it('should not add the backwards compatibility CSS when no `@import "tailwindcss } `), ).toMatchInlineSnapshot(` - "@variant foo { + "@custom-variant foo { } @utility bar { @@ -287,7 +289,7 @@ it('should not add the backwards compatibility CSS when another `@import "tailwi await migrate(css` @import 'tailwindcss/theme'; - @variant foo { + @custom-variant foo { } @utility bar { @@ -305,7 +307,7 @@ it('should not add the backwards compatibility CSS when another `@import "tailwi ).toMatchInlineSnapshot(` "@import 'tailwindcss/theme'; - @variant foo { + @custom-variant foo { } @utility bar { diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.ts similarity index 81% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.ts index 8677d6af6..d32d96979 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-preflight.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-preflight.ts @@ -1,18 +1,20 @@ import dedent from 'dedent' import postcss, { type Plugin, type Root } from 'postcss' -import { keyPathToCssProperty } from '../../../tailwindcss/src/compat/apply-config-to-theme' -import type { Config } from '../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import { toKeyPath } from '../../../tailwindcss/src/utils/to-key-path' -import * as ValueParser from '../../../tailwindcss/src/value-parser' +import { keyPathToCssProperty } from '../../../../tailwindcss/src/compat/apply-config-to-theme' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { toKeyPath } from '../../../../tailwindcss/src/utils/to-key-path' +import * as ValueParser from '../../../../tailwindcss/src/value-parser' +import { walk, WalkAction } from '../../../../tailwindcss/src/walk' +import * as version from '../../utils/version' // Defaults in v4 -const DEFAULT_BORDER_COLOR = 'currentColor' +const DEFAULT_BORDER_COLOR = 'currentcolor' const css = dedent const BORDER_COLOR_COMPATIBILITY_CSS = css` /* - The default border color has changed to \`currentColor\` in Tailwind CSS v4, + The default border color has changed to \`currentcolor\` in Tailwind CSS v4, so we've added these compatibility styles to make sure everything still looks the same as it did with Tailwind CSS v3. @@ -34,18 +36,23 @@ export function migratePreflight({ designSystem, userConfig, }: { - designSystem: DesignSystem - userConfig?: Config + designSystem: DesignSystem | null + userConfig?: Config | null }): Plugin { // @ts-expect-error let defaultBorderColor = userConfig?.theme?.borderColor?.DEFAULT function canResolveThemeValue(path: string) { + if (!designSystem) return false let variable = `--${keyPathToCssProperty(toKeyPath(path))}` as const return Boolean(designSystem.theme.get([variable])) } function migrate(root: Root) { + // CSS for backwards compatibility with v3 should only injected in v3 + // projects and not v4 projects. + if (!version.isMajor(3)) return + let isTailwindRoot = false root.walkAtRules('import', (node) => { if ( @@ -78,7 +85,7 @@ export function migratePreflight({ return defaultBorderColor } else { if (path === 'borderColor.DEFAULT') { - return 'var(--color-gray-200, currentColor)' + return 'var(--color-gray-200, currentcolor)' } } return null @@ -111,7 +118,7 @@ function substituteFunctionsInValue( ast: ValueParser.ValueAstNode[], handle: (value: string, fallback?: string) => string | null, ) { - ValueParser.walk(ast, (node, { replaceWith }) => { + walk(ast, (node) => { if (node.kind === 'function' && node.value === 'theme') { if (node.nodes.length < 1) return @@ -149,7 +156,7 @@ function substituteFunctionsInValue( fallbackValues.length > 0 ? handle(path, ValueParser.toCss(fallbackValues)) : handle(path) if (replacement === null) return - replaceWith(ValueParser.parse(replacement)) + return WalkAction.Replace(ValueParser.parse(replacement)) } }) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.test.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.test.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-tailwind-directives.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-tailwind-directives.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.test.ts similarity index 89% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.test.ts index ce3940bc9..f0b4844ad 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.test.ts @@ -35,11 +35,11 @@ it('should migrate `theme(…)` to `var(…)`', async () => { } `), ).toMatchInlineSnapshot(` - "@media theme(--breakpoint-sm) { + "@media --theme(--breakpoint-sm) { .foo { background-color: var(--color-red-900); - color: theme(--color-red-900 / 75%); - border-color: theme(--color-red-200 / 75%); + color: --theme(--color-red-900 / 75%); + border-color: --theme(--color-red-200 / 75%); } }" `) diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.ts similarity index 80% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.ts index 558e6d0cb..8285def1e 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/migrate-theme-to-var.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-theme-to-var.ts @@ -1,11 +1,11 @@ import { type Plugin } from 'postcss' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import { Convert, createConverter } from '../template/codemods/theme-to-var' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { Convert, createConverter } from '../template/migrate-theme-to-var' export function migrateThemeToVar({ designSystem, }: { - designSystem?: DesignSystem + designSystem?: DesignSystem | null } = {}): Plugin { return { postcssPlugin: '@tailwindcss/upgrade/migrate-theme-to-var', diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-variants-directive.test.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.test.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-variants-directive.test.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.test.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/migrate-variants-directive.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/codemods/migrate-variants-directive.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/migrate-variants-directive.ts diff --git a/packages/@tailwindcss-upgrade/src/codemods/css/migrate.ts b/packages/@tailwindcss-upgrade/src/codemods/css/migrate.ts new file mode 100644 index 000000000..cb9150851 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/css/migrate.ts @@ -0,0 +1,57 @@ +import postcss from 'postcss' +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import { Stylesheet } from '../../stylesheet' +import type { JSConfigMigration } from '../config/migrate-js-config' +import { migrateAtApply } from './migrate-at-apply' +import { migrateAtLayerUtilities } from './migrate-at-layer-utilities' +import { migrateConfig } from './migrate-config' +import { migrateImport } from './migrate-import' +import { migrateMediaScreen } from './migrate-media-screen' +import { migrateMissingLayers } from './migrate-missing-layers' +import { migratePreflight } from './migrate-preflight' +import { migrateTailwindDirectives } from './migrate-tailwind-directives' +import { migrateThemeToVar } from './migrate-theme-to-var' +import { migrateVariantsDirective } from './migrate-variants-directive' + +export interface MigrateOptions { + newPrefix: string | null + designSystem: DesignSystem | null + userConfig: Config | null + configFilePath: string | null + jsConfigMigration: JSConfigMigration | null +} + +export async function migrate(stylesheet: Stylesheet, options: MigrateOptions) { + if (!stylesheet.file) { + throw new Error('Cannot migrate a stylesheet without a file path') + } + + if (!stylesheet.canMigrate) return + + await migrateContents(stylesheet, options) +} + +export async function migrateContents( + stylesheet: Stylesheet | string, + options: MigrateOptions, + file?: string, +) { + if (typeof stylesheet === 'string') { + stylesheet = await Stylesheet.fromString(stylesheet) + stylesheet.file = file ?? null + } + + return postcss() + .use(migrateImport()) + .use(migrateAtApply(options)) + .use(migrateMediaScreen(options)) + .use(migrateVariantsDirective()) + .use(migrateAtLayerUtilities(stylesheet)) + .use(migrateMissingLayers()) + .use(migrateTailwindDirectives(options)) + .use(migrateConfig(stylesheet, options)) + .use(migratePreflight(options)) + .use(migrateThemeToVar(options)) + .process(stylesheet.root, { from: stylesheet.file ?? undefined }) +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/sort-buckets.ts b/packages/@tailwindcss-upgrade/src/codemods/css/sort-buckets.ts similarity index 94% rename from packages/@tailwindcss-upgrade/src/codemods/sort-buckets.ts rename to packages/@tailwindcss-upgrade/src/codemods/css/sort-buckets.ts index b99badeb0..22b0581dd 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/sort-buckets.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/css/sort-buckets.ts @@ -1,6 +1,6 @@ import postcss, { type AtRule, type ChildNode, type Comment, type Plugin, type Root } from 'postcss' -import { DefaultMap } from '../../../tailwindcss/src/utils/default-map' -import { walk, WalkAction } from '../utils/walk' +import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map' +import { walk, WalkAction } from '../../utils/walk' const BUCKET_ORDER = [ // Imports @@ -10,7 +10,7 @@ const BUCKET_ORDER = [ 'config', // @config 'plugin', // @plugin 'source', // @source - 'variant', // @variant + 'custom-variant', // @custom-variant 'theme', // @theme // Styles @@ -75,7 +75,7 @@ export function sortBuckets(): Plugin { // Known at-rules else if ( node.type === 'atrule' && - ['config', 'plugin', 'source', 'theme', 'utility', 'variant'].includes(node.name) + ['config', 'plugin', 'source', 'theme', 'utility', 'custom-variant'].includes(node.name) ) { injectInto(node.name, node) } diff --git a/packages/@tailwindcss-upgrade/src/codemods/css/split.ts b/packages/@tailwindcss-upgrade/src/codemods/css/split.ts new file mode 100644 index 000000000..146d30733 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/css/split.ts @@ -0,0 +1,257 @@ +import postcss from 'postcss' +import { DefaultMap } from '../../../../tailwindcss/src/utils/default-map' +import { Stylesheet, type StylesheetId } from '../../stylesheet' +import { walk, WalkAction } from '../../utils/walk' + +export async function split(stylesheets: Stylesheet[]) { + let stylesheetsById = new Map() + let stylesheetsByFile = new Map() + + for (let sheet of stylesheets) { + stylesheetsById.set(sheet.id, sheet) + + if (sheet.file) { + stylesheetsByFile.set(sheet.file, sheet) + } + } + + // Keep track of sheets that contain `@utility` rules + let requiresSplit = new Set() + + for (let sheet of stylesheets) { + // Root files don't need to be split + if (sheet.isTailwindRoot) continue + + let containsUtility = false + let containsUnsafe = sheet.layers().size > 0 + + walk(sheet.root, (node) => { + if (node.type === 'atrule' && node.name === 'utility') { + containsUtility = true + } + + // Safe to keep without splitting + else if ( + // An `@import "…" layer(…)` is safe + (node.type === 'atrule' && node.name === 'import' && node.params.includes('layer(')) || + // @layer blocks are safe + (node.type === 'atrule' && node.name === 'layer') || + // Comments are safe + node.type === 'comment' + ) { + return WalkAction.Skip + } + + // Everything else is not safe, and requires a split + else { + containsUnsafe = true + } + + // We already know we need to split this sheet + if (containsUtility && containsUnsafe) { + return WalkAction.Stop + } + + return WalkAction.Skip + }) + + if (containsUtility && containsUnsafe) { + requiresSplit.add(sheet) + } + } + + // Split every imported stylesheet into two parts + let utilitySheets = new Map() + + for (let sheet of stylesheets) { + // Ignore stylesheets that were not imported + if (!sheet.file) continue + if (sheet.parents.size === 0) continue + + // Skip stylesheets that don't have utilities + // and don't have any children that have utilities + if (!requiresSplit.has(sheet)) { + if (!Array.from(sheet.descendants()).some((child) => requiresSplit.has(child))) { + continue + } + } + + let utilities = postcss.root() + + walk(sheet.root, (node) => { + if (node.type !== 'atrule') return + if (node.name !== 'utility') return + + // `append` will move this node from the original sheet + // to the new utilities sheet + utilities.append(node) + + return WalkAction.Skip + }) + + let newFileName = sheet.file.replace(/\.css$/, '.utilities.css') + + let counter = 0 + + // If we already have a utility sheet with this name, we need to rename it + while (stylesheetsByFile.has(newFileName)) { + counter += 1 + newFileName = sheet.file.replace(/\.css$/, `.utilities.${counter}.css`) + } + + let utilitySheet = await Stylesheet.fromRoot(utilities, newFileName) + + utilitySheet.extension = counter > 0 ? `.utilities.${counter}.css` : `.utilities.css` + + utilitySheets.set(sheet, utilitySheet) + stylesheetsById.set(utilitySheet.id, utilitySheet) + } + + // Make sure the utility sheets are linked to one another + for (let [normalSheet, utilitySheet] of utilitySheets) { + for (let parent of normalSheet.parents) { + let utilityParent = utilitySheets.get(parent.item) + if (!utilityParent) continue + utilitySheet.parents.add({ + item: utilityParent, + meta: parent.meta, + }) + } + + for (let child of normalSheet.children) { + let utilityChild = utilitySheets.get(child.item) + if (!utilityChild) continue + utilitySheet.children.add({ + item: utilityChild, + meta: child.meta, + }) + } + } + + for (let sheet of stylesheets) { + let utilitySheet = utilitySheets.get(sheet) + let utilityImports: Set = new Set() + + for (let node of sheet.importRules) { + let sheetId = node.raws.tailwind_destination_sheet_id as StylesheetId | undefined + + // This import rule does not point to a stylesheet + // which likely means it points to `node_modules` + if (!sheetId) continue + + let originalDestination = stylesheetsById.get(sheetId) + + // This import points to a stylesheet that no longer exists which likely + // means it was removed by the optimizer this will be cleaned up later + if (!originalDestination) continue + + let utilityDestination = utilitySheets.get(originalDestination) + + // A utility sheet doesn't exist for this import so it doesn't need + // to be processed + if (!utilityDestination) continue + + let match = node.params.match(/(['"])(.*)\1/) + if (!match) return + + let quote = match[1] + let id = match[2] + + let newFile = id.replace(/\.css$/, utilityDestination.extension!) + + // The import will just point to the new file without any media queries, + // layers, or other conditions because `@utility` MUST be top-level. + let newImport = node.clone({ + params: `${quote}${newFile}${quote}`, + raws: { + tailwind_injected_layer: node.raws.tailwind_injected_layer, + tailwind_original_params: `${quote}${id}${quote}`, + tailwind_destination_sheet_id: utilityDestination.id, + }, + }) + + if (utilitySheet) { + // If this import is intended to go into the utility sheet + // we'll collect it into a list to add later. If we don't' + // we'll end up adding them in reverse order. + utilityImports.add(newImport) + } else { + // This import will go immediately after the original import + node.after(newImport) + } + } + + // Add imports to the top of the utility sheet if necessary + if (utilitySheet && utilityImports.size > 0) { + utilitySheet.root.prepend(Array.from(utilityImports)) + } + } + + // Tracks the at rules that import a given stylesheet + let importNodes = new DefaultMap>(() => new Set()) + + for (let sheet of stylesheetsById.values()) { + for (let node of sheet.importRules) { + let sheetId = node.raws.tailwind_destination_sheet_id as StylesheetId | undefined + + // This import rule does not point to a stylesheet + if (!sheetId) continue + + let destination = stylesheetsById.get(sheetId) + + // This import rule does not point to a stylesheet that exists + // We'll remove it later + if (!destination) continue + + importNodes.get(destination).add(node) + } + } + + // At this point we've created many `{name}.utilities.css` files. + // If the original file _becomes_ empty after splitting that means that + // dedicated utility file is not required and we can move the utilities + // back to the original file. + // + // This could be done in one step but separating them makes it easier to + // reason about since the stylesheets are in a consistent state before we + // perform any cleanup tasks. + let list: Stylesheet[] = [] + + for (let sheet of stylesheets.slice()) { + for (let child of sheet.descendants()) { + list.push(child) + } + + list.push(sheet) + } + + for (let sheet of list) { + let utilitySheet = utilitySheets.get(sheet) + + // This sheet was not split so there's nothing to do + if (!utilitySheet) continue + + // This sheet did not become empty + if (!sheet.isEmpty) continue + + // We have a sheet that became empty after splitting + // 1. Replace the sheet with it's utility sheet content + sheet.root = utilitySheet.root + + // 2. Rewrite imports in parent sheets to point to the original sheet + // Ideally this wouldn't need to be _undone_ but instead only done once at the end + for (let node of importNodes.get(utilitySheet)) { + node.params = node.raws.tailwind_original_params as any + } + + // 3. Remove the original import from the non-utility sheet + for (let node of importNodes.get(sheet)) { + node.remove() + } + + // 3. Mark the utility sheet for removal + utilitySheets.delete(sheet) + } + + stylesheets.push(...utilitySheets.values()) +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/candidates.test.ts b/packages/@tailwindcss-upgrade/src/codemods/template/candidates.test.ts new file mode 100644 index 000000000..2e4d150aa --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/candidates.test.ts @@ -0,0 +1,84 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import { expect, test } from 'vitest' +import { spliceChangesIntoString } from '../../utils/splice-changes-into-string' +import { extractRawCandidates } from './candidates' + +let html = String.raw + +test('extracts candidates with positions from a template', async () => { + let content = html` +
+ +
+ ` + let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { + base: __dirname, + }) + + let candidates = await extractRawCandidates(content, 'html') + let validCandidates = candidates.filter( + ({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0, + ) + + expect(validCandidates).toMatchInlineSnapshot(` + [ + { + "end": 28, + "rawCandidate": "bg-blue-500", + "start": 17, + }, + { + "end": 51, + "rawCandidate": "hover:focus:text-white", + "start": 29, + }, + { + "end": 63, + "rawCandidate": "[color:red]", + "start": 52, + }, + { + "end": 98, + "rawCandidate": "bg-blue-500", + "start": 87, + }, + { + "end": 109, + "rawCandidate": "text-white", + "start": 99, + }, + ] + `) +}) + +test('replaces the right positions for a candidate', async () => { + let content = html` +

🤠👋

+
+ ` + + let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { + base: __dirname, + }) + + let candidates = await extractRawCandidates(content, 'html') + + let candidate = candidates.find( + ({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0, + )! + + let migrated = spliceChangesIntoString(content, [ + { + start: candidate.start, + end: candidate.end, + replacement: 'flex', + }, + ]) + + expect(migrated).toMatchInlineSnapshot(` + " +

🤠👋

+
+ " + `) +}) diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/candidates.ts b/packages/@tailwindcss-upgrade/src/codemods/template/candidates.ts new file mode 100644 index 000000000..88b1e4e03 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/candidates.ts @@ -0,0 +1,43 @@ +import { Scanner } from '@tailwindcss/oxide' +import { cloneCandidate, type Candidate } from '../../../../tailwindcss/src/candidate' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' + +export async function extractRawCandidates( + content: string, + extension: string = 'html', +): Promise<{ rawCandidate: string; start: number; end: number }[]> { + let scanner = new Scanner({}) + let result = scanner.getCandidatesWithPositions({ content, extension }) + + let candidates: { rawCandidate: string; start: number; end: number }[] = [] + for (let { candidate: rawCandidate, position: start } of result) { + candidates.push({ rawCandidate, start, end: start + rawCandidate.length }) + } + return candidates +} + +// Create a basic stripped candidate without variants or important flag +export function baseCandidate(candidate: T) { + let base = cloneCandidate(candidate) + + base.important = false + base.variants = [] + + return base +} + +export function parseCandidate(designSystem: DesignSystem, input: string) { + return designSystem.parseCandidate( + designSystem.theme.prefix && !input.startsWith(`${designSystem.theme.prefix}:`) + ? `${designSystem.theme.prefix}:${input}` + : input, + ) +} + +export function printUnprefixedCandidate(designSystem: DesignSystem, candidate: Candidate) { + let candidateString = designSystem.printCandidate(candidate) + + return designSystem.theme.prefix && candidateString.startsWith(`${designSystem.theme.prefix}:`) + ? candidateString.slice(designSystem.theme.prefix.length + 1) + : candidateString +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts new file mode 100644 index 000000000..7cfb16c75 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts @@ -0,0 +1,154 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import { describe, expect, test, vi } from 'vitest' +import * as versions from '../../utils/version' +import { migrateCandidate } from './migrate' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) + +const css = String.raw + +describe('is-safe-migration', async () => { + let designSystem = await __unstable__loadDesignSystem( + css` + @import 'tailwindcss'; + + /* TODO(perf): Only here to speed up the tests */ + @theme { + --*: initial; + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + } + `, + { base: __dirname }, + ) + + test.each([ + [`let notBorder = !border \n`, '!border'], + [`{ "foo": !border.something + ""}\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + [`
\n`, '!border'], + + [`let notShadow = shadow \n`, 'shadow'], + [`{ "foo": shadow.something + ""}\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'shadow'], + [`
\n`, 'flex-grow'], + [`
\n`, 'flex-shrink'], + [`
\n`, 'flex-shrink'], + [`
\n`, 'flex-shrink'], + + // Next.js Image placeholder cases + [``, 'blur'], + [``, 'blur'], + [``, 'blur'], + + // https://github.com/tailwindlabs/tailwindcss/issues/17974 + ['
', '!duration'], + ['
', '!duration'], + ['
', '!visible'], + + // Alpine/Livewire wire:… + ['', 'blur'], + + // Vue 3 events + [`emit('blur', props.modelValue)\n`, 'blur'], + [`$emit('blur', props.modelValue)\n`, 'blur'], + + // JavaScript / TypeScript + [`document.addEventListener('blur',handleBlur)`, 'blur'], + [`document.addEventListener('blur', handleBlur)`, 'blur'], + + [`function foo({ outline = true })`, 'outline'], + [`function foo({ before = false, outline = true })`, 'outline'], + [`function foo({before=false,outline=true })`, 'outline'], + [`function foo({outline=true })`, 'outline'], + // https://github.com/tailwindlabs/tailwindcss/issues/18675 + [ + // With default value + `function foo({ size = "1.25rem", digit, outline = true, textClass = "", className = "" })`, + 'outline', + ], + [ + // Without default value + `function foo({ size = "1.25rem", digit, outline, textClass = "", className = "" })`, + 'outline', + ], + [ + // As the last argument + `function foo({ size = "1.25rem", digit, outline })`, + 'outline', + ], + [ + // As the last argument, but there is technically another `"` on the same line + `function foo({ size = "1.25rem", digit, outline }): { return "foo" }`, + 'outline', + ], + [ + // Tricky quote balancing + `function foo({ before = "'", outline, after = "'" }): { return "foo" }`, + 'outline', + ], + + [`function foo(blur, foo)`, 'blur'], + [`function foo(blur,foo)`, 'blur'], + + // shadcn/ui variants + [` -
- ` - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - let candidates = await extractRawCandidates(content, 'html') - let validCandidates = candidates.filter( - ({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0, - ) - - expect(validCandidates).toMatchInlineSnapshot(` - [ - { - "end": 28, - "rawCandidate": "bg-blue-500", - "start": 17, - }, - { - "end": 51, - "rawCandidate": "hover:focus:text-white", - "start": 29, - }, - { - "end": 63, - "rawCandidate": "[color:red]", - "start": 52, - }, - { - "end": 98, - "rawCandidate": "bg-blue-500", - "start": 87, - }, - { - "end": 109, - "rawCandidate": "text-white", - "start": 99, - }, - ] - `) -}) - -test('replaces the right positions for a candidate', async () => { - let content = html` -

🤠👋

-
- ` - - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - let candidates = await extractRawCandidates(content, 'html') - - let candidate = candidates.find( - ({ rawCandidate }) => designSystem.parseCandidate(rawCandidate).length > 0, - )! - - let migrated = spliceChangesIntoString(content, [ - { - start: candidate.start, - end: candidate.end, - replacement: 'flex', - }, - ]) - - expect(migrated).toMatchInlineSnapshot(` - " -

🤠👋

-
- " - `) -}) - -const candidates = [ - // Arbitrary candidates - ['[color:red]', '[color:red]'], - ['[color:red]/50', '[color:red]/50'], - ['[color:red]/[0.5]', '[color:red]/[0.5]'], - ['[color:red]/50!', '[color:red]/50!'], - ['![color:red]/50', '[color:red]/50!'], - ['[color:red]/[0.5]!', '[color:red]/[0.5]!'], - - // Static candidates - ['box-border', 'box-border'], - ['underline!', 'underline!'], - ['!underline', 'underline!'], - ['-inset-full', '-inset-full'], - - // Functional candidates - ['bg-red-500', 'bg-red-500'], - ['bg-red-500/50', 'bg-red-500/50'], - ['bg-red-500/[0.5]', 'bg-red-500/[0.5]'], - ['bg-red-500!', 'bg-red-500!'], - ['!bg-red-500', 'bg-red-500!'], - ['bg-[#0088cc]/50', 'bg-[#0088cc]/50'], - ['bg-[#0088cc]/[0.5]', 'bg-[#0088cc]/[0.5]'], - ['bg-[#0088cc]!', 'bg-[#0088cc]!'], - ['!bg-[#0088cc]', 'bg-[#0088cc]!'], - ['bg-[var(--spacing)-1px]', 'bg-[var(--spacing)-1px]'], - ['bg-[var(--spacing)_-_1px]', 'bg-[var(--spacing)-1px]'], - ['bg-[-1px_-1px]', 'bg-[-1px_-1px]'], - ['p-[round(to-zero,1px)]', 'p-[round(to-zero,1px)]'], - ['w-1/2', 'w-1/2'], - ['p-[calc((100vw-theme(maxWidth.2xl))_/_2)]', 'p-[calc((100vw-theme(maxWidth.2xl))/2)]'], - - // Keep spaces in strings - ['content-["hello_world"]', 'content-["hello_world"]'], - ['content-[____"hello_world"___]', 'content-["hello_world"]'], - - // Do not escape underscores for url() and CSS variable in var() - ['bg-[no-repeat_url(/image_13.png)]', 'bg-[no-repeat_url(/image_13.png)]'], - [ - 'bg-[var(--spacing-0_5,_var(--spacing-1_5,_3rem))]', - 'bg-(--spacing-0_5,var(--spacing-1_5,3rem))', - ], -] - -const variants = [ - ['', ''], // no variant - ['*:', '*:'], - ['focus:', 'focus:'], - ['group-focus:', 'group-focus:'], - - ['hover:focus:', 'hover:focus:'], - ['hover:group-focus:', 'hover:group-focus:'], - ['group-hover:focus:', 'group-hover:focus:'], - ['group-hover:group-focus:', 'group-hover:group-focus:'], - - ['min-[10px]:', 'min-[10px]:'], - - // Normalize spaces - ['min-[calc(1000px_+_12em)]:', 'min-[calc(1000px+12em)]:'], - ['min-[calc(1000px_+12em)]:', 'min-[calc(1000px+12em)]:'], - ['min-[calc(1000px+_12em)]:', 'min-[calc(1000px+12em)]:'], - ['min-[calc(1000px___+___12em)]:', 'min-[calc(1000px+12em)]:'], - - ['peer-[&_p]:', 'peer-[&_p]:'], - ['peer-[&_p]:hover:', 'peer-[&_p]:hover:'], - ['hover:peer-[&_p]:', 'hover:peer-[&_p]:'], - ['hover:peer-[&_p]:focus:', 'hover:peer-[&_p]:focus:'], - ['peer-[&:hover]:peer-[&_p]:', 'peer-[&:hover]:peer-[&_p]:'], - - ['[p]:', '[p]:'], - ['[_p_]:', '[p]:'], - ['has-[p]:', 'has-[p]:'], - ['has-[_p_]:', 'has-[p]:'], - - // Simplify `&:is(p)` to `p` - ['[&:is(p)]:', '[p]:'], - ['[&:is(_p_)]:', '[p]:'], - ['has-[&:is(p)]:', 'has-[p]:'], - ['has-[&:is(_p_)]:', 'has-[p]:'], -] - -let combinations: [string, string][] = [] - -for (let [inputVariant, outputVariant] of variants) { - for (let [inputCandidate, outputCandidate] of candidates) { - combinations.push([`${inputVariant}${inputCandidate}`, `${outputVariant}${outputCandidate}`]) - } -} - -describe('printCandidate()', () => { - test.each(combinations)('%s -> %s', async (candidate: string, result: string) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - let candidates = designSystem.parseCandidate(candidate) - - // Sometimes we will have a functional and a static candidate for the same - // raw input string (e.g. `-inset-full`). Dedupe in this case. - let cleaned = new Set([...candidates].map((c) => printCandidate(designSystem, c))) - - expect([...cleaned]).toEqual([result]) - }) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/candidates.ts b/packages/@tailwindcss-upgrade/src/template/candidates.ts deleted file mode 100644 index 6a0ab65bd..000000000 --- a/packages/@tailwindcss-upgrade/src/template/candidates.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { Scanner } from '@tailwindcss/oxide' -import type { Candidate, Variant } from '../../../tailwindcss/src/candidate' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import * as ValueParser from '../../../tailwindcss/src/value-parser' - -export async function extractRawCandidates( - content: string, - extension: string = 'html', -): Promise<{ rawCandidate: string; start: number; end: number }[]> { - let scanner = new Scanner({}) - let result = scanner.getCandidatesWithPositions({ content, extension }) - - let candidates: { rawCandidate: string; start: number; end: number }[] = [] - for (let { candidate: rawCandidate, position: start } of result) { - candidates.push({ rawCandidate, start, end: start + rawCandidate.length }) - } - return candidates -} - -export function printCandidate(designSystem: DesignSystem, candidate: Candidate) { - let parts: string[] = [] - - for (let variant of candidate.variants) { - parts.unshift(printVariant(variant)) - } - - // Handle prefix - if (designSystem.theme.prefix) { - parts.unshift(designSystem.theme.prefix) - } - - let base: string = '' - - // Handle static - if (candidate.kind === 'static') { - base += candidate.root - } - - // Handle functional - if (candidate.kind === 'functional') { - base += candidate.root - - if (candidate.value) { - if (candidate.value.kind === 'arbitrary') { - if (candidate.value !== null) { - let isVarValue = isVar(candidate.value.value) - let value = isVarValue ? candidate.value.value.slice(4, -1) : candidate.value.value - let [open, close] = isVarValue ? ['(', ')'] : ['[', ']'] - - if (candidate.value.dataType) { - base += `-${open}${candidate.value.dataType}:${printArbitraryValue(value)}${close}` - } else { - base += `-${open}${printArbitraryValue(value)}${close}` - } - } - } else if (candidate.value.kind === 'named') { - base += `-${candidate.value.value}` - } - } - } - - // Handle arbitrary - if (candidate.kind === 'arbitrary') { - base += `[${candidate.property}:${printArbitraryValue(candidate.value)}]` - } - - // Handle modifier - if (candidate.kind === 'arbitrary' || candidate.kind === 'functional') { - if (candidate.modifier) { - let isVarValue = isVar(candidate.modifier.value) - let value = isVarValue ? candidate.modifier.value.slice(4, -1) : candidate.modifier.value - let [open, close] = isVarValue ? ['(', ')'] : ['[', ']'] - - if (candidate.modifier.kind === 'arbitrary') { - base += `/${open}${printArbitraryValue(value)}${close}` - } else if (candidate.modifier.kind === 'named') { - base += `/${candidate.modifier.value}` - } - } - } - - // Handle important - if (candidate.important) { - base += '!' - } - - parts.push(base) - - return parts.join(':') -} - -function printVariant(variant: Variant) { - // Handle static variants - if (variant.kind === 'static') { - return variant.root - } - - // Handle arbitrary variants - if (variant.kind === 'arbitrary') { - return `[${printArbitraryValue(simplifyArbitraryVariant(variant.selector))}]` - } - - let base: string = '' - - // Handle functional variants - if (variant.kind === 'functional') { - base += variant.root - if (variant.value) { - if (variant.value.kind === 'arbitrary') { - let isVarValue = isVar(variant.value.value) - let value = isVarValue ? variant.value.value.slice(4, -1) : variant.value.value - let [open, close] = isVarValue ? ['(', ')'] : ['[', ']'] - - base += `-${open}${printArbitraryValue(value)}${close}` - } else if (variant.value.kind === 'named') { - base += `-${variant.value.value}` - } - } - } - - // Handle compound variants - if (variant.kind === 'compound') { - base += variant.root - base += '-' - base += printVariant(variant.variant) - } - - // Handle modifiers - if (variant.kind === 'functional' || variant.kind === 'compound') { - if (variant.modifier) { - if (variant.modifier.kind === 'arbitrary') { - base += `/[${printArbitraryValue(variant.modifier.value)}]` - } else if (variant.modifier.kind === 'named') { - base += `/${variant.modifier.value}` - } - } - } - - return base -} - -function printArbitraryValue(input: string) { - let ast = ValueParser.parse(input) - - let drop = new Set() - - ValueParser.walk(ast, (node, { parent }) => { - let parentArray = parent === null ? ast : (parent.nodes ?? []) - - // Handle operators (e.g.: inside of `calc(…)`) - if ( - node.kind === 'word' && - // Operators - (node.value === '+' || node.value === '-' || node.value === '*' || node.value === '/') - ) { - let idx = parentArray.indexOf(node) ?? -1 - - // This should not be possible - if (idx === -1) return - - let previous = parentArray[idx - 1] - if (previous?.kind !== 'separator' || previous.value !== ' ') return - - let next = parentArray[idx + 1] - if (next?.kind !== 'separator' || next.value !== ' ') return - - drop.add(previous) - drop.add(next) - } - - // The value parser handles `/` as a separator in some scenarios. E.g.: - // `theme(colors.red/50%)`. Because of this, we have to handle this case - // separately. - else if (node.kind === 'separator' && node.value.trim() === '/') { - node.value = '/' - } - - // Leading and trailing whitespace - else if (node.kind === 'separator' && node.value.length > 0 && node.value.trim() === '') { - if (parentArray[0] === node || parentArray[parentArray.length - 1] === node) { - drop.add(node) - } - } - - // Whitespace around `,` separators can be removed. - // E.g.: `min(1px , 2px)` -> `min(1px,2px)` - else if (node.kind === 'separator' && node.value.trim() === ',') { - node.value = ',' - } - }) - - if (drop.size > 0) { - ValueParser.walk(ast, (node, { replaceWith }) => { - if (drop.has(node)) { - drop.delete(node) - replaceWith([]) - } - }) - } - - recursivelyEscapeUnderscores(ast) - - return ValueParser.toCss(ast) -} - -function simplifyArbitraryVariant(input: string) { - let ast = ValueParser.parse(input) - - // &:is(…) - if ( - ast.length === 3 && - // & - ast[0].kind === 'word' && - ast[0].value === '&' && - // : - ast[1].kind === 'separator' && - ast[1].value === ':' && - // is(…) - ast[2].kind === 'function' && - ast[2].value === 'is' - ) { - return ValueParser.toCss(ast[2].nodes) - } - - return input -} - -function recursivelyEscapeUnderscores(ast: ValueParser.ValueAstNode[]) { - for (let node of ast) { - switch (node.kind) { - case 'function': { - if (node.value === 'url' || node.value.endsWith('_url')) { - // Don't decode underscores in url() but do decode the function name - node.value = escapeUnderscore(node.value) - break - } - - if ( - node.value === 'var' || - node.value.endsWith('_var') || - node.value === 'theme' || - node.value.endsWith('_theme') - ) { - // Don't decode underscores in the first argument of var() and theme() - // but do decode the function name - node.value = escapeUnderscore(node.value) - for (let i = 0; i < node.nodes.length; i++) { - if (i == 0 && node.nodes[i].kind === 'word') { - continue - } - recursivelyEscapeUnderscores([node.nodes[i]]) - } - break - } - - node.value = escapeUnderscore(node.value) - recursivelyEscapeUnderscores(node.nodes) - break - } - case 'separator': - node.value = escapeUnderscore(node.value) - break - case 'word': { - // Dashed idents and variables `var(--my-var)` and `--my-var` should not - // have underscores escaped - if (node.value[0] !== '-' && node.value[1] !== '-') { - node.value = escapeUnderscore(node.value) - } - break - } - default: - never(node) - } - } -} - -function isVar(value: string) { - let ast = ValueParser.parse(value) - return ast.length === 1 && ast[0].kind === 'function' && ast[0].value === 'var' -} - -function never(value: never): never { - throw new Error(`Unexpected value: ${value}`) -} - -function escapeUnderscore(value: string): string { - return value - .replaceAll('_', String.raw`\_`) // Escape underscores to keep them as-is - .replaceAll(' ', '_') // Replace spaces with underscores -} diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.test.ts deleted file mode 100644 index 62355dc6f..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { __unstable__loadDesignSystem } from '@tailwindcss/node' -import { expect, test } from 'vitest' -import { arbitraryValueToBareValue } from './arbitrary-value-to-bare-value' - -test.each([ - ['aspect-[12/34]', 'aspect-12/34'], - ['aspect-[1.2/34]', 'aspect-[1.2/34]'], - ['col-start-[7]', 'col-start-7'], - ['flex-[2]', 'flex-2'], // `flex` is implemented as static and functional utilities - - ['grid-cols-[subgrid]', 'grid-cols-subgrid'], - ['grid-rows-[subgrid]', 'grid-rows-subgrid'], - - // Only 50-200% (inclusive) are valid: - // https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch#percentage - ['font-stretch-[50%]', 'font-stretch-50%'], - ['font-stretch-[50.5%]', 'font-stretch-[50.5%]'], - ['font-stretch-[201%]', 'font-stretch-[201%]'], - ['font-stretch-[49%]', 'font-stretch-[49%]'], - // Should stay as-is - ['font-stretch-[1/2]', 'font-stretch-[1/2]'], - - // Bare value with % is valid for these utilities - ['from-[28%]', 'from-28%'], - ['via-[28%]', 'via-28%'], - ['to-[28%]', 'to-28%'], - ['from-[28.5%]', 'from-[28.5%]'], - ['via-[28.5%]', 'via-[28.5%]'], - ['to-[28.5%]', 'to-[28.5%]'], - - // This test in itself is a bit flawed because `text-[1/2]` currently - // generates something. Converting it to `text-1/2` doesn't produce anything. - ['text-[1/2]', 'text-[1/2]'], - - ['data-[selected]:flex', 'data-selected:flex'], - ['data-[foo=bar]:flex', 'data-[foo=bar]:flex'], - - ['supports-[gap]:flex', 'supports-gap:flex'], - ['supports-[display:grid]:flex', 'supports-[display:grid]:flex'], - - ['group-data-[selected]:flex', 'group-data-selected:flex'], - ['group-data-[foo=bar]:flex', 'group-data-[foo=bar]:flex'], - ['group-has-data-[selected]:flex', 'group-has-data-selected:flex'], - - ['aria-[selected]:flex', 'aria-[selected]:flex'], - ['aria-[selected="true"]:flex', 'aria-selected:flex'], - ['aria-[selected*="true"]:flex', 'aria-[selected*="true"]:flex'], - - ['group-aria-[selected]:flex', 'group-aria-[selected]:flex'], - ['group-aria-[selected="true"]:flex', 'group-aria-selected:flex'], - ['group-has-aria-[selected]:flex', 'group-has-aria-[selected]:flex'], - - ['max-lg:hover:data-[selected]:flex!', 'max-lg:hover:data-selected:flex!'], - [ - 'data-[selected]:aria-[selected="true"]:aspect-[12/34]', - 'data-selected:aria-selected:aspect-12/34', - ], -])('%s => %s', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - expect(arbitraryValueToBareValue(designSystem, {}, candidate)).toEqual(result) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.ts b/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.ts deleted file mode 100644 index 77a303dc3..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/arbitrary-value-to-bare-value.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { parseCandidate, type Candidate, type Variant } from '../../../../tailwindcss/src/candidate' -import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../../tailwindcss/src/design-system' -import { isPositiveInteger } from '../../../../tailwindcss/src/utils/infer-data-type' -import { segment } from '../../../../tailwindcss/src/utils/segment' -import { printCandidate } from '../candidates' - -export function arbitraryValueToBareValue( - designSystem: DesignSystem, - _userConfig: Config, - rawCandidate: string, -): string { - for (let candidate of parseCandidate(rawCandidate, designSystem)) { - let clone = structuredClone(candidate) - let changed = false - - // Convert [subgrid] to subgrid - if ( - clone.kind === 'functional' && - clone.value?.kind === 'arbitrary' && - clone.value.value === 'subgrid' && - (clone.root === 'grid-cols' || clone.root == 'grid-rows') - ) { - changed = true - clone.value = { - kind: 'named', - value: 'subgrid', - fraction: null, - } - } - - // Convert utilities that accept bare values ending in % - if ( - clone.kind === 'functional' && - clone.value?.kind === 'arbitrary' && - clone.value.dataType === null && - (clone.root === 'from' || - clone.root === 'via' || - clone.root === 'to' || - clone.root === 'font-stretch') - ) { - if (clone.value.value.endsWith('%') && isPositiveInteger(clone.value.value.slice(0, -1))) { - let percentage = parseInt(clone.value.value) - if ( - clone.root === 'from' || - clone.root === 'via' || - clone.root === 'to' || - (clone.root === 'font-stretch' && percentage >= 50 && percentage <= 200) - ) { - changed = true - clone.value = { - kind: 'named', - value: clone.value.value, - fraction: null, - } - } - } - } - - // Convert arbitrary values with positive integers to bare values - // Convert arbitrary values with fractions to bare values - else if ( - clone.kind === 'functional' && - clone.value?.kind === 'arbitrary' && - clone.value.dataType === null - ) { - let parts = segment(clone.value.value, '/') - if (parts.every((part) => isPositiveInteger(part))) { - changed = true - - let currentValue = clone.value - let currentModifier = clone.modifier - - // E.g.: `col-start-[12]` - // ^^ - if (parts.length === 1) { - clone.value = { - kind: 'named', - value: clone.value.value, - fraction: null, - } - } - - // E.g.: `aspect-[12/34]` - // ^^ ^^ - else { - clone.value = { - kind: 'named', - value: parts[0], - fraction: clone.value.value, - } - clone.modifier = { - kind: 'named', - value: parts[1], - } - } - - // Double check that the new value compiles correctly - if (designSystem.compileAstNodes(clone).length === 0) { - clone.value = currentValue - clone.modifier = currentModifier - changed = false - } - } - } - - for (let variant of variants(clone)) { - // Convert `data-[selected]` to `data-selected` - if ( - variant.kind === 'functional' && - variant.root === 'data' && - variant.value?.kind === 'arbitrary' && - !variant.value.value.includes('=') - ) { - changed = true - variant.value = { - kind: 'named', - value: variant.value.value, - } - } - - // Convert `aria-[selected="true"]` to `aria-selected` - else if ( - variant.kind === 'functional' && - variant.root === 'aria' && - variant.value?.kind === 'arbitrary' && - (variant.value.value.endsWith('=true') || - variant.value.value.endsWith('="true"') || - variant.value.value.endsWith("='true'")) - ) { - let [key, _value] = segment(variant.value.value, '=') - if ( - // aria-[foo~="true"] - key[key.length - 1] === '~' || - // aria-[foo|="true"] - key[key.length - 1] === '|' || - // aria-[foo^="true"] - key[key.length - 1] === '^' || - // aria-[foo$="true"] - key[key.length - 1] === '$' || - // aria-[foo*="true"] - key[key.length - 1] === '*' - ) { - continue - } - - changed = true - variant.value = { - kind: 'named', - value: variant.value.value.slice(0, variant.value.value.indexOf('=')), - } - } - - // Convert `supports-[gap]` to `supports-gap` - else if ( - variant.kind === 'functional' && - variant.root === 'supports' && - variant.value?.kind === 'arbitrary' && - /^[a-z-][a-z0-9-]*$/i.test(variant.value.value) - ) { - changed = true - variant.value = { - kind: 'named', - value: variant.value.value, - } - } - } - - return changed ? printCandidate(designSystem, clone) : rawCandidate - } - - return rawCandidate -} - -function* variants(candidate: Candidate) { - function* inner(variant: Variant): Iterable { - yield variant - if (variant.kind === 'compound') { - yield* inner(variant.variant) - } - } - - for (let variant of candidate.variants) { - yield* inner(variant) - } -} diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.test.ts deleted file mode 100644 index 3c42903c3..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { __unstable__loadDesignSystem } from '@tailwindcss/node' -import { expect, test } from 'vitest' -import { bgGradient } from './bg-gradient' - -test.each([ - ['bg-gradient-to-t', 'bg-linear-to-t'], - ['bg-gradient-to-tr', 'bg-linear-to-tr'], - ['bg-gradient-to-r', 'bg-linear-to-r'], - ['bg-gradient-to-br', 'bg-linear-to-br'], - ['bg-gradient-to-b', 'bg-linear-to-b'], - ['bg-gradient-to-bl', 'bg-linear-to-bl'], - ['bg-gradient-to-l', 'bg-linear-to-l'], - ['bg-gradient-to-tl', 'bg-linear-to-tl'], - - ['max-lg:hover:bg-gradient-to-t', 'max-lg:hover:bg-linear-to-t'], -])('%s => %s', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - expect(bgGradient(designSystem, {}, candidate)).toEqual(result) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.ts b/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.ts deleted file mode 100644 index 02ad5590b..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/bg-gradient.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../../tailwindcss/src/design-system' -import { printCandidate } from '../candidates' - -const DIRECTIONS = ['t', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl'] - -export function bgGradient( - designSystem: DesignSystem, - _userConfig: Config, - rawCandidate: string, -): string { - for (let candidate of designSystem.parseCandidate(rawCandidate)) { - if (candidate.kind === 'static' && candidate.root.startsWith('bg-gradient-to-')) { - let direction = candidate.root.slice(15) - - if (!DIRECTIONS.includes(direction)) { - continue - } - - return printCandidate(designSystem, { - ...candidate, - root: `bg-linear-to-${direction}`, - }) - } - } - return rawCandidate -} diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/important.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/important.test.ts deleted file mode 100644 index 60d78aa53..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/important.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { __unstable__loadDesignSystem } from '@tailwindcss/node' -import { expect, test } from 'vitest' -import { important } from './important' - -test.each([ - ['!flex', 'flex!'], - ['min-[calc(1000px+12em)]:!flex', 'min-[calc(1000px+12em)]:flex!'], - ['md:!block', 'md:block!'], - - // Does not change non-important candidates - ['bg-blue-500', 'bg-blue-500'], - ['min-[calc(1000px+12em)]:flex', 'min-[calc(1000px+12em)]:flex'], -])('%s => %s', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - expect( - important(designSystem, {}, candidate, { - contents: `"${candidate}"`, - start: 1, - end: candidate.length + 1, - }), - ).toEqual(result) -}) - -test('does not match false positives', async () => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - expect( - important(designSystem, {}, '!border', { - contents: `let notBorder = !border\n`, - start: 16, - end: 16 + '!border'.length, - }), - ).toEqual('!border') -}) - -test('does not match false positives', async () => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - function shouldNotDetect(example: string, candidate = '!border') { - expect( - important(designSystem, {}, candidate, { - contents: example, - start: example.indexOf(candidate), - end: example.indexOf(candidate) + candidate.length, - }), - ).toEqual('!border') - } - - shouldNotDetect(`let notBorder = !border \n`) - shouldNotDetect(`{ "foo": !border.something + ""}\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) - shouldNotDetect(`
\n`) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/important.ts b/packages/@tailwindcss-upgrade/src/template/codemods/important.ts deleted file mode 100644 index 4f2c6871f..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/important.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { parseCandidate } from '../../../../tailwindcss/src/candidate' -import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../../tailwindcss/src/design-system' -import { printCandidate } from '../candidates' -import { isSafeMigration } from '../is-safe-migration' - -// In v3 the important modifier `!` sits in front of the utility itself, not -// before any of the variants. In v4, we want it to be at the end of the utility -// so that it's always in the same location regardless of whether you used -// variants or not. -// -// So this: -// -// !flex md:!block -// -// Should turn into: -// -// flex! md:block! -export function important( - designSystem: DesignSystem, - _userConfig: Config, - rawCandidate: string, - location?: { - contents: string - start: number - end: number - }, -): string { - nextCandidate: for (let candidate of parseCandidate(rawCandidate, designSystem)) { - if (candidate.important && candidate.raw[candidate.raw.length - 1] !== '!') { - // The important migration is one of the most broad migrations with a high - // potential of matching false positives since `!` is a valid character in - // most programming languages. Since v4 is technically backward compatible - // with v3 in that it can read `!` in the front of the utility too, we err - // on the side of caution and only migrate candidates that we are certain - // are inside of a string. - if (location && !isSafeMigration(location)) { - continue nextCandidate - } - - // The printCandidate function will already put the exclamation mark in - // the right place, so we just need to mark this candidate as requiring a - // migration. - return printCandidate(designSystem, candidate) - } - } - - return rawCandidate -} diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.test.ts deleted file mode 100644 index ec38d0864..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { __unstable__loadDesignSystem } from '@tailwindcss/node' -import { expect, test } from 'vitest' -import { handleEmptyArbitraryValues } from './handle-empty-arbitrary-values' -import { modernizeArbitraryValues } from './modernize-arbitrary-values' -import { prefix } from './prefix' - -test.each([ - // Arbitrary variants - ['[[data-visible]]:flex', 'data-visible:flex'], - ['[&[data-visible]]:flex', 'data-visible:flex'], - ['[[data-visible]&]:flex', 'data-visible:flex'], - ['[&>[data-visible]]:flex', '*:data-visible:flex'], - ['[&_>_[data-visible]]:flex', '*:data-visible:flex'], - ['[&>*]:flex', '*:flex'], - ['[&_>_*]:flex', '*:flex'], - - ['[&_[data-visible]]:flex', '**:data-visible:flex'], - ['[&_*]:flex', '**:flex'], - - ['[&:first-child]:flex', 'first:flex'], - ['[&:not(:first-child)]:flex', 'not-first:flex'], - - // in-* variants - ['[p_&]:flex', 'in-[p]:flex'], - ['[.foo_&]:flex', 'in-[.foo]:flex'], - ['[[data-visible]_&]:flex', 'in-data-visible:flex'], - // Multiple selectors, should stay as-is - ['[[data-foo][data-bar]_&]:flex', '[[data-foo][data-bar]_&]:flex'], - // Using `>` instead of ` ` should not be transformed: - ['[figure>&]:my-0', '[figure>&]:my-0'], - // Some extreme examples of what happens in the wild: - ['group-[]:flex', 'in-[.group]:flex'], - ['group-[]/name:flex', 'in-[.group\\/name]:flex'], - - // Migrate `peer-[]` to a parsable `peer-[&]` instead: - ['peer-[]:flex', 'peer-[&]:flex'], - ['peer-[]/name:flex', 'peer-[&]/name:flex'], - - // These shouldn't happen in the real world (because compound variants are - // new). But this could happen once we allow codemods to run in v4+ projects. - ['has-group-[]:flex', 'has-in-[.group]:flex'], - ['has-group-[]/name:flex', 'has-in-[.group\\/name]:flex'], - ['not-group-[]:flex', 'not-in-[.group]:flex'], - ['not-group-[]/name:flex', 'not-in-[.group\\/name]:flex'], - - // nth-child - ['[&:nth-child(2)]:flex', 'nth-2:flex'], - ['[&:not(:nth-child(2))]:flex', 'not-nth-2:flex'], - - ['[&:nth-child(-n+3)]:flex', 'nth-[-n+3]:flex'], - ['[&:not(:nth-child(-n+3))]:flex', 'not-nth-[-n+3]:flex'], - ['[&:nth-child(-n_+_3)]:flex', 'nth-[-n+3]:flex'], - ['[&:not(:nth-child(-n_+_3))]:flex', 'not-nth-[-n+3]:flex'], - - // nth-last-child - ['[&:nth-last-child(2)]:flex', 'nth-last-2:flex'], - ['[&:not(:nth-last-child(2))]:flex', 'not-nth-last-2:flex'], - - ['[&:nth-last-child(-n+3)]:flex', 'nth-last-[-n+3]:flex'], - ['[&:not(:nth-last-child(-n+3))]:flex', 'not-nth-last-[-n+3]:flex'], - ['[&:nth-last-child(-n_+_3)]:flex', 'nth-last-[-n+3]:flex'], - ['[&:not(:nth-last-child(-n_+_3))]:flex', 'not-nth-last-[-n+3]:flex'], - - // nth-child odd/even - ['[&:nth-child(odd)]:flex', 'odd:flex'], - ['[&:not(:nth-child(odd))]:flex', 'even:flex'], - ['[&:nth-child(even)]:flex', 'even:flex'], - ['[&:not(:nth-child(even))]:flex', 'odd:flex'], - - // Keep multiple attribute selectors as-is - ['[[data-visible][data-dark]]:flex', '[[data-visible][data-dark]]:flex'], - - // Complex attribute selectors with operators, quotes and insensitivity flags - ['[[data-url*="example"]]:flex', 'data-[url*="example"]:flex'], - ['[[data-url$=".com"_i]]:flex', 'data-[url$=".com"_i]:flex'], - ['[[data-url$=.com_i]]:flex', 'data-[url$=.com_i]:flex'], - - // Attribute selector wrapped in `&:is(…)` - ['[&:is([data-visible])]:flex', 'data-visible:flex'], - - // Compound arbitrary variants - ['has-[[data-visible]]:flex', 'has-data-visible:flex'], - ['has-[&:is([data-visible])]:flex', 'has-data-visible:flex'], - ['has-[&>[data-visible]]:flex', 'has-[&>[data-visible]]:flex'], - - ['has-[[data-slot=description]]:flex', 'has-data-[slot=description]:flex'], - ['has-[&:is([data-slot=description])]:flex', 'has-data-[slot=description]:flex'], - - ['has-[[aria-visible="true"]]:flex', 'has-aria-visible:flex'], - ['has-[[aria-visible]]:flex', 'has-aria-[visible]:flex'], - - ['has-[&:not(:nth-child(even))]:flex', 'has-odd:flex'], -])('%s => %s (%#)', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { - base: __dirname, - }) - - expect( - [handleEmptyArbitraryValues, modernizeArbitraryValues].reduce( - (acc, step) => step(designSystem, {}, acc), - candidate, - ), - ).toEqual(result) -}) - -test.each([ - // Should not prefix classes in arbitrary values - ['[.foo_&]:tw-flex', 'tw:in-[.foo]:flex'], - - // Should migrate `.group` classes - ['group-[]:tw-flex', 'tw:in-[.tw\\:group]:flex'], - ['group-[]/name:tw-flex', 'tw:in-[.tw\\:group\\/name]:flex'], - - // Migrate `peer-[]` to a parsable `peer-[&]` instead: - ['peer-[]:tw-flex', 'tw:peer-[&]:flex'], - ['peer-[]/name:tw-flex', 'tw:peer-[&]/name:flex'], - - // However, `.group` inside of an arbitrary variant should not be prefixed: - ['[.group_&]:tw-flex', 'tw:in-[.group]:flex'], - - // These shouldn't happen in the real world (because compound variants are - // new). But this could happen once we allow codemods to run in v4+ projects. - ['has-group-[]:tw-flex', 'tw:has-in-[.tw\\:group]:flex'], - ['has-group-[]/name:tw-flex', 'tw:has-in-[.tw\\:group\\/name]:flex'], - ['not-group-[]:tw-flex', 'tw:not-in-[.tw\\:group]:flex'], - ['not-group-[]/name:tw-flex', 'tw:not-in-[.tw\\:group\\/name]:flex'], -])('%s => %s (%#)', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss" prefix(tw);', { - base: __dirname, - }) - - expect( - [handleEmptyArbitraryValues, prefix, modernizeArbitraryValues].reduce( - (acc, step) => step(designSystem, { prefix: 'tw-' }, acc), - candidate, - ), - ).toEqual(result) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.ts b/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.ts deleted file mode 100644 index 3d90351b1..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/modernize-arbitrary-values.ts +++ /dev/null @@ -1,402 +0,0 @@ -import SelectorParser from 'postcss-selector-parser' -import { parseCandidate, type Candidate, type Variant } from '../../../../tailwindcss/src/candidate' -import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../../tailwindcss/src/design-system' -import { isPositiveInteger } from '../../../../tailwindcss/src/utils/infer-data-type' -import { printCandidate } from '../candidates' - -function memcpy(target: T, source: U): U { - // Clear out the target object, otherwise inspecting the final object will - // look very confusing. - for (let key in target) delete target[key] - - return Object.assign(target, source) -} - -export function modernizeArbitraryValues( - designSystem: DesignSystem, - _userConfig: Config, - rawCandidate: string, -): string { - for (let candidate of parseCandidate(rawCandidate, designSystem)) { - let clone = structuredClone(candidate) - let changed = false - - for (let [variant, parent] of variants(clone)) { - // Forward modifier from the root to the compound variant - if ( - variant.kind === 'compound' && - (variant.root === 'has' || variant.root === 'not' || variant.root === 'in') - ) { - if (variant.modifier !== null) { - if ('modifier' in variant.variant) { - variant.variant.modifier = variant.modifier - variant.modifier = null - } - } - } - - // Promote `group-[]:flex` to `in-[.group]:flex` - // ^^ Yes, this is empty - // Promote `group-[]/name:flex` to `in-[.group\/name]:flex` - if ( - variant.kind === 'compound' && - variant.root === 'group' && - variant.variant.kind === 'arbitrary' && - variant.variant.selector === '&' - ) { - // `group-[]` - if (variant.modifier === null) { - changed = true - memcpy( - variant, - designSystem.parseVariant( - designSystem.theme.prefix - ? `in-[.${designSystem.theme.prefix}\\:group]` - : 'in-[.group]', - ), - ) - } - - // `group-[]/name` - else if (variant.modifier.kind === 'named') { - changed = true - memcpy( - variant, - designSystem.parseVariant( - designSystem.theme.prefix - ? `in-[.${designSystem.theme.prefix}\\:group\\/${variant.modifier.value}]` - : `in-[.group\\/${variant.modifier.value}]`, - ), - ) - } - continue - } - - // Expecting an arbitrary variant - if (variant.kind === 'arbitrary') { - // Expecting a non-relative arbitrary variant - if (variant.relative) continue - - let ast = SelectorParser().astSync(variant.selector) - - // Expecting a single selector node - if (ast.nodes.length !== 1) continue - - // `[&>*]` can be replaced with `*` - if ( - // Only top-level, so `has-[&>*]` is not supported - parent === null && - // [&_>_*]:flex - // ^ ^ ^ - ast.nodes[0].length === 3 && - ast.nodes[0].nodes[0].type === 'nesting' && - ast.nodes[0].nodes[0].value === '&' && - ast.nodes[0].nodes[1].type === 'combinator' && - ast.nodes[0].nodes[1].value === '>' && - ast.nodes[0].nodes[2].type === 'universal' - ) { - changed = true - memcpy(variant, designSystem.parseVariant('*')) - continue - } - - // `[&_*]` can be replaced with `**` - if ( - // Only top-level, so `has-[&_*]` is not supported - parent === null && - // [&_*]:flex - // ^ ^ - ast.nodes[0].length === 3 && - ast.nodes[0].nodes[0].type === 'nesting' && - ast.nodes[0].nodes[0].value === '&' && - ast.nodes[0].nodes[1].type === 'combinator' && - ast.nodes[0].nodes[1].value === ' ' && - ast.nodes[0].nodes[2].type === 'universal' - ) { - changed = true - memcpy(variant, designSystem.parseVariant('**')) - continue - } - - // `in-*` variant. If the selector ends with ` &`, we can convert it to an - // `in-*` variant. - // - // E.g.: `[[data-visible]_&]` => `in-data-visible` - if ( - // Only top-level, so `in-[&_[data-visible]]` is not supported - parent === null && - // [[data-visible]___&]:flex - // ^^^^^^^^^^^^^^ ^ ^ - ast.nodes[0].nodes.length === 3 && - ast.nodes[0].nodes[1].type === 'combinator' && - ast.nodes[0].nodes[1].value === ' ' && - ast.nodes[0].nodes[2].type === 'nesting' - ) { - ast.nodes[0].nodes.pop() // Remove the nesting node - ast.nodes[0].nodes.pop() // Remove the combinator - - changed = true - // When handling a compound like `in-[[data-visible]]`, we will first - // handle `[[data-visible]]`, then the parent `in-*` part. This means - // that we can convert `[[data-visible]_&]` to `in-[[data-visible]]`. - // - // Later this gets converted to `in-data-visible`. - memcpy(variant, designSystem.parseVariant(`in-[${ast.toString()}]`)) - continue - } - - let prefixedVariant: Variant | null = null - - // Handling a child combinator. E.g.: `[&>[data-visible]]` => `*:data-visible` - if ( - // Only top-level, so `has-[&>[data-visible]]` is not supported - parent === null && - // [&_>_[data-visible]]:flex - // ^ ^ ^^^^^^^^^^^^^^ - ast.nodes[0].length === 3 && - ast.nodes[0].nodes[0].type === 'nesting' && - ast.nodes[0].nodes[0].value === '&' && - ast.nodes[0].nodes[1].type === 'combinator' && - ast.nodes[0].nodes[1].value === '>' && - ast.nodes[0].nodes[2].type === 'attribute' - ) { - ast.nodes[0].nodes = [ast.nodes[0].nodes[2]] - prefixedVariant = designSystem.parseVariant('*') - } - - // Handling a grand child combinator. E.g.: `[&_[data-visible]]` => `**:data-visible` - if ( - // Only top-level, so `has-[&_[data-visible]]` is not supported - parent === null && - // [&_[data-visible]]:flex - // ^ ^^^^^^^^^^^^^^ - ast.nodes[0].length === 3 && - ast.nodes[0].nodes[0].type === 'nesting' && - ast.nodes[0].nodes[0].value === '&' && - ast.nodes[0].nodes[1].type === 'combinator' && - ast.nodes[0].nodes[1].value === ' ' && - ast.nodes[0].nodes[2].type === 'attribute' - ) { - ast.nodes[0].nodes = [ast.nodes[0].nodes[2]] - prefixedVariant = designSystem.parseVariant('**') - } - - // Filter out `&`. E.g.: `&[data-foo]` => `[data-foo]` - let selectorNodes = ast.nodes[0].filter((node) => node.type !== 'nesting') - - // Expecting a single selector (normal selector or attribute selector) - if (selectorNodes.length !== 1) continue - - let target = selectorNodes[0] - if (target.type === 'pseudo' && target.value === ':is') { - // Expecting a single selector node - if (target.nodes.length !== 1) continue - - // Expecting a single attribute selector - if (target.nodes[0].nodes.length !== 1) continue - - // Unwrap the selector from inside `&:is(…)` - target = target.nodes[0].nodes[0] - } - - // Expecting a pseudo selector - if (target.type === 'pseudo') { - let targetNode = target - let compoundNot = false - if (target.value === ':not') { - compoundNot = true - if (target.nodes.length !== 1) continue - if (target.nodes[0].type !== 'selector') continue - if (target.nodes[0].nodes.length !== 1) continue - if (target.nodes[0].nodes[0].type !== 'pseudo') continue - - targetNode = target.nodes[0].nodes[0] - } - - let newVariant = ((value) => { - // - if (value === ':first-letter') return 'first-letter' - else if (value === ':first-line') return 'first-line' - // - else if (value === ':file-selector-button') return 'file' - else if (value === ':placeholder') return 'placeholder' - else if (value === ':backdrop') return 'backdrop' - // Positional - else if (value === ':first-child') return 'first' - else if (value === ':last-child') return 'last' - else if (value === ':only-child') return 'only' - else if (value === ':first-of-type') return 'first-of-type' - else if (value === ':last-of-type') return 'last-of-type' - else if (value === ':only-of-type') return 'only-of-type' - // State - else if (value === ':visited') return 'visited' - else if (value === ':target') return 'target' - // Forms - else if (value === ':default') return 'default' - else if (value === ':checked') return 'checked' - else if (value === ':indeterminate') return 'indeterminate' - else if (value === ':placeholder-shown') return 'placeholder-shown' - else if (value === ':autofill') return 'autofill' - else if (value === ':optional') return 'optional' - else if (value === ':required') return 'required' - else if (value === ':valid') return 'valid' - else if (value === ':invalid') return 'invalid' - else if (value === ':in-range') return 'in-range' - else if (value === ':out-of-range') return 'out-of-range' - else if (value === ':read-only') return 'read-only' - // Content - else if (value === ':empty') return 'empty' - // Interactive - else if (value === ':focus-within') return 'focus-within' - else if (value === ':focus') return 'focus' - else if (value === ':focus-visible') return 'focus-visible' - else if (value === ':active') return 'active' - else if (value === ':enabled') return 'enabled' - else if (value === ':disabled') return 'disabled' - // - if ( - value === ':nth-child' && - targetNode.nodes.length === 1 && - targetNode.nodes[0].nodes.length === 1 && - targetNode.nodes[0].nodes[0].type === 'tag' && - targetNode.nodes[0].nodes[0].value === 'odd' - ) { - if (compoundNot) { - compoundNot = false - return 'even' - } - return 'odd' - } - if ( - value === ':nth-child' && - targetNode.nodes.length === 1 && - targetNode.nodes[0].nodes.length === 1 && - targetNode.nodes[0].nodes[0].type === 'tag' && - targetNode.nodes[0].nodes[0].value === 'even' - ) { - if (compoundNot) { - compoundNot = false - return 'odd' - } - return 'even' - } - - for (let [selector, variantName] of [ - [':nth-child', 'nth'], - [':nth-last-child', 'nth-last'], - [':nth-of-type', 'nth-of-type'], - [':nth-last-of-type', 'nth-of-last-type'], - ]) { - if (value === selector && targetNode.nodes.length === 1) { - if ( - targetNode.nodes[0].nodes.length === 1 && - targetNode.nodes[0].nodes[0].type === 'tag' && - isPositiveInteger(targetNode.nodes[0].nodes[0].value) - ) { - return `${variantName}-${targetNode.nodes[0].nodes[0].value}` - } - - return `${variantName}-[${targetNode.nodes[0].toString()}]` - } - } - - return null - })(targetNode.value) - - if (newVariant === null) continue - - // Add `not-` prefix - if (compoundNot) newVariant = `not-${newVariant}` - - let parsed = designSystem.parseVariant(newVariant) - if (parsed === null) continue - - // Update original variant - changed = true - memcpy(variant, parsed) - } - - // Expecting an attribute selector - else if (target.type === 'attribute') { - // Attribute selectors - let attributeKey = target.attribute - let attributeValue = target.value - ? target.quoted - ? `${target.quoteMark}${target.value}${target.quoteMark}` - : target.value - : null - - // Insensitive attribute selectors. E.g.: `[data-foo="value" i]` - // ^ - if (target.insensitive && attributeValue) { - attributeValue += ' i' - } - - let operator = target.operator ?? '=' - - // Migrate `data-*` - if (attributeKey.startsWith('data-')) { - changed = true - attributeKey = attributeKey.slice(5) // Remove `data-` - memcpy(variant, { - kind: 'functional', - root: 'data', - modifier: null, - value: - attributeValue === null - ? { kind: 'named', value: attributeKey } - : { kind: 'arbitrary', value: `${attributeKey}${operator}${attributeValue}` }, - } satisfies Variant) - } - - // Migrate `aria-*` - else if (attributeKey.startsWith('aria-')) { - changed = true - attributeKey = attributeKey.slice(5) // Remove `aria-` - memcpy(variant, { - kind: 'functional', - root: 'aria', - modifier: null, - value: - attributeValue === null - ? { kind: 'arbitrary', value: attributeKey } // aria-[foo] - : operator === '=' && target.value === 'true' && !target.insensitive - ? { kind: 'named', value: attributeKey } // aria-[foo="true"] or aria-[foo='true'] or aria-[foo=true] - : { kind: 'arbitrary', value: `${attributeKey}${operator}${attributeValue}` }, // aria-[foo~="true"], aria-[foo|="true"], … - } satisfies Variant) - } - } - - if (prefixedVariant) { - let idx = clone.variants.indexOf(variant) - if (idx === -1) continue - - // Ensure we inject the prefixed variant - clone.variants.splice(idx, 1, variant, prefixedVariant) - } - } - } - - return changed ? printCandidate(designSystem, clone) : rawCandidate - } - - return rawCandidate -} - -function* variants(candidate: Candidate) { - function* inner( - variant: Variant, - parent: Extract | null = null, - ): Iterable<[Variant, Extract | null]> { - yield [variant, parent] - - if (variant.kind === 'compound') { - yield* inner(variant.variant, variant) - } - } - - for (let variant of candidate.variants) { - yield* inner(variant, null) - } -} diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts deleted file mode 100644 index f5f2a41db..000000000 --- a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { __unstable__loadDesignSystem } from '@tailwindcss/node' -import { expect, test } from 'vitest' -import { themeToVar } from './theme-to-var' - -let css = String.raw - -test.each([ - // Keep candidates that don't contain `theme(…)` or `theme(…, …)` - ['[color:red]', '[color:red]'], - - // Handle special cases around `.1` in the `theme(…)` - ['[--value:theme(spacing.1)]', '[--value:calc(var(--spacing)*1)]'], - ['[--value:theme(fontSize.xs.1.lineHeight)]', '[--value:var(--text-xs--line-height)]'], - ['[--value:theme(spacing[1.25])]', '[--value:calc(var(--spacing)*1.25)]'], - - // Should not convert invalid spacing values to calc - ['[--value:theme(spacing[1.1])]', '[--value:theme(spacing[1.1])]'], - - // Convert to `var(…)` if we can resolve the path - ['[color:theme(colors.red.500)]', '[color:var(--color-red-500)]'], // Arbitrary property - ['[color:theme(colors.red.500)]/50', '[color:var(--color-red-500)]/50'], // Arbitrary property + modifier - ['bg-[theme(colors.red.500)]', 'bg-(--color-red-500)'], // Arbitrary value - ['bg-[size:theme(spacing.4)]', 'bg-[size:calc(var(--spacing)*4)]'], // Arbitrary value + data type hint - - // Convert to `var(…)` if we can resolve the path, but keep fallback values - ['bg-[theme(colors.red.500,red)]', 'bg-(--color-red-500,red)'], - - // Keep `theme(…)` if we can't resolve the path - ['bg-[theme(colors.foo.1000)]', 'bg-[theme(colors.foo.1000)]'], - - // Keep `theme(…)` if we can't resolve the path, but still try to convert the - // fallback value. - [ - 'bg-[theme(colors.foo.1000,theme(colors.red.500))]', - 'bg-[theme(colors.foo.1000,var(--color-red-500))]', - ], - - // Use `theme(…)` (deeply nested) inside of a `calc(…)` function - ['text-[calc(theme(fontSize.xs)*2)]', 'text-[calc(var(--text-xs)*2)]'], - - // Multiple `theme(… / …)` calls should result in modern syntax of `theme(…)` - // - Can't convert to `var(…)` because that would lose the modifier. - // - Can't convert to a candidate modifier because there are multiple - // `theme(…)` calls. - // - // If we really want to, we can make a fancy migration that tries to move it - // to a candidate modifier _if_ all `theme(…)` calls use the same modifier. - [ - '[color:theme(colors.red.500/50,theme(colors.blue.500/50))]', - '[color:theme(--color-red-500/50,theme(--color-blue-500/50))]', - ], - [ - '[color:theme(colors.red.500/50,theme(colors.blue.500/50))]/50', - '[color:theme(--color-red-500/50,theme(--color-blue-500/50))]/50', - ], - - // Convert the `theme(…)`, but try to move the inline modifier (e.g. `50%`), - // to a candidate modifier. - // Arbitrary property, with simple percentage modifier - ['[color:theme(colors.red.500/75%)]', '[color:var(--color-red-500)]/75'], - - // Arbitrary property, with numbers (0-1) without a unit - ['[color:theme(colors.red.500/.12)]', '[color:var(--color-red-500)]/12'], - ['[color:theme(colors.red.500/0.12)]', '[color:var(--color-red-500)]/12'], - - // Arbitrary property, with more complex modifier (we only allow whole numbers - // as bare modifiers). Convert the complex numbers to arbitrary values instead. - ['[color:theme(colors.red.500/12.34%)]', '[color:var(--color-red-500)]/[12.34%]'], - ['[color:theme(colors.red.500/var(--opacity))]', '[color:var(--color-red-500)]/(--opacity)'], - ['[color:theme(colors.red.500/.12345)]', '[color:var(--color-red-500)]/[12.345]'], - ['[color:theme(colors.red.500/50.25%)]', '[color:var(--color-red-500)]/[50.25%]'], - - // Arbitrary value - ['bg-[theme(colors.red.500/75%)]', 'bg-(--color-red-500)/75'], - ['bg-[theme(colors.red.500/12.34%)]', 'bg-(--color-red-500)/[12.34%]'], - - // Arbitrary property that already contains a modifier - ['[color:theme(colors.red.500/50%)]/50', '[color:theme(--color-red-500/50%)]/50'], - - // Values that don't contain only `theme(…)` calls should not be converted to - // use a modifier since the color is not the whole value. - [ - 'shadow-[shadow:inset_0px_1px_theme(colors.white/15%)]', - 'shadow-[shadow:inset_0px_1px_theme(--color-white/15%)]', - ], - - // Arbitrary value, where the candidate already contains a modifier - // This should still migrate the `theme(…)` syntax to the modern syntax. - ['bg-[theme(colors.red.500/50%)]/50', 'bg-[theme(--color-red-500/50%)]/50'], - - // Variants, we can't use `var(…)` especially inside of `@media(…)`. We can - // still upgrade the `theme(…)` to the modern syntax. - ['max-[theme(screens.lg)]:flex', 'max-[theme(--breakpoint-lg)]:flex'], - // There are no variables for `--spacing` multiples, so we can't convert this - ['max-[theme(spacing.4)]:flex', 'max-[theme(spacing.4)]:flex'], - - // This test in itself doesn't make much sense. But we need to make sure - // that this doesn't end up as the modifier in the candidate itself. - ['max-[theme(spacing.4/50)]:flex', 'max-[theme(spacing.4/50)]:flex'], - - // `theme(…)` calls in another CSS function is replaced correctly. - // Additionally we remove unnecessary whitespace. - [ - 'grid-cols-[min(50%_,_theme(spacing.80))_auto]', - 'grid-cols-[min(50%,calc(var(--spacing)*80))_auto]', - ], - - // `theme(…)` calls valid in v3, but not in v4 should still be converted. - ['[--foo:theme(transitionDuration.500)]', '[--foo:theme(transitionDuration.500)]'], - - // Renamed theme keys - ['max-w-[theme(screens.md)]', 'max-w-(--breakpoint-md)'], - ['w-[theme(maxWidth.md)]', 'w-(--container-md)'], - - // Invalid cases - ['[--foo:theme(colors.red.500/50/50)]', '[--foo:theme(colors.red.500/50/50)]'], - ['[--foo:theme(colors.red.500/50/50)]/50', '[--foo:theme(colors.red.500/50/50)]/50'], - - // Partially invalid cases - [ - '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]', - '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]', - ], - [ - '[--foo:theme(colors.red.500/50/50)_theme(colors.blue.200)]/50', - '[--foo:theme(colors.red.500/50/50)_var(--color-blue-200)]/50', - ], -])('%s => %s', async (candidate, result) => { - let designSystem = await __unstable__loadDesignSystem( - css` - @import 'tailwindcss'; - `, - { - base: __dirname, - }, - ) - - expect(themeToVar(designSystem, {}, candidate)).toEqual(result) -}) - -test('extended space scale converts to var or calc', async () => { - let designSystem = await __unstable__loadDesignSystem( - css` - @import 'tailwindcss'; - @theme { - --spacing-2: 2px; - --spacing-miami: 0.875rem; - } - `, - { - base: __dirname, - }, - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.1)]')).toEqual( - '[--value:calc(var(--spacing)*1)]', - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.2)]')).toEqual( - '[--value:var(--spacing-2)]', - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.miami)]')).toEqual( - '[--value:var(--spacing-miami)]', - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.nyc)]')).toEqual( - '[--value:theme(spacing.nyc)]', - ) -}) - -test('custom space scale converts to var', async () => { - let designSystem = await __unstable__loadDesignSystem( - css` - @import 'tailwindcss'; - @theme { - --spacing-*: initial; - --spacing-1: 0.25rem; - --spacing-2: 0.5rem; - } - `, - { - base: __dirname, - }, - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.1)]')).toEqual( - '[--value:var(--spacing-1)]', - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.2)]')).toEqual( - '[--value:var(--spacing-2)]', - ) - expect(themeToVar(designSystem, {}, '[--value:theme(spacing.3)]')).toEqual( - '[--value:theme(spacing.3)]', - ) -}) diff --git a/packages/@tailwindcss-upgrade/src/template/is-safe-migration.ts b/packages/@tailwindcss-upgrade/src/template/is-safe-migration.ts deleted file mode 100644 index 046ee10e6..000000000 --- a/packages/@tailwindcss-upgrade/src/template/is-safe-migration.ts +++ /dev/null @@ -1,62 +0,0 @@ -const QUOTES = ['"', "'", '`'] -const LOGICAL_OPERATORS = ['&&', '||', '?', '===', '==', '!=', '!==', '>', '>=', '<', '<='] -const CONDITIONAL_TEMPLATE_SYNTAX = [ - // Vue - /v-else-if=['"]$/, - /v-if=['"]$/, - /v-show=['"]$/, - - // Alpine - /x-if=['"]$/, - /x-show=['"]$/, -] - -export function isSafeMigration(location: { contents: string; start: number; end: number }) { - let currentLineBeforeCandidate = '' - for (let i = location.start - 1; i >= 0; i--) { - let char = location.contents.at(i)! - if (char === '\n') { - break - } - currentLineBeforeCandidate = char + currentLineBeforeCandidate - } - let currentLineAfterCandidate = '' - for (let i = location.end; i < location.contents.length; i++) { - let char = location.contents.at(i)! - if (char === '\n') { - break - } - currentLineAfterCandidate += char - } - - // Heuristic 1: Require the candidate to be inside quotes - let isQuoteBeforeCandidate = QUOTES.some((quote) => currentLineBeforeCandidate.includes(quote)) - let isQuoteAfterCandidate = QUOTES.some((quote) => currentLineAfterCandidate.includes(quote)) - if (!isQuoteBeforeCandidate || !isQuoteAfterCandidate) { - return false - } - - // Heuristic 2: Disallow object access immediately following the candidate - if (currentLineAfterCandidate[0] === '.') { - return false - } - - // Heuristic 3: Disallow logical operators preceding or following the candidate - for (let operator of LOGICAL_OPERATORS) { - if ( - currentLineAfterCandidate.trim().startsWith(operator) || - currentLineBeforeCandidate.trim().endsWith(operator) - ) { - return false - } - } - - // Heuristic 4: Disallow conditional template syntax - for (let rule of CONDITIONAL_TEMPLATE_SYNTAX) { - if (rule.test(currentLineBeforeCandidate)) { - return false - } - } - - return true -} diff --git a/packages/@tailwindcss-upgrade/src/template/migrate.ts b/packages/@tailwindcss-upgrade/src/template/migrate.ts deleted file mode 100644 index 7fdc164f2..000000000 --- a/packages/@tailwindcss-upgrade/src/template/migrate.ts +++ /dev/null @@ -1,104 +0,0 @@ -import fs from 'node:fs/promises' -import path, { extname } from 'node:path' -import type { Config } from '../../../tailwindcss/src/compat/plugin-api' -import type { DesignSystem } from '../../../tailwindcss/src/design-system' -import { extractRawCandidates } from './candidates' -import { arbitraryValueToBareValue } from './codemods/arbitrary-value-to-bare-value' -import { automaticVarInjection } from './codemods/automatic-var-injection' -import { bgGradient } from './codemods/bg-gradient' -import { handleEmptyArbitraryValues } from './codemods/handle-empty-arbitrary-values' -import { important } from './codemods/important' -import { legacyArbitraryValues } from './codemods/legacy-arbitrary-values' -import { legacyClasses } from './codemods/legacy-classes' -import { maxWidthScreen } from './codemods/max-width-screen' -import { modernizeArbitraryValues } from './codemods/modernize-arbitrary-values' -import { prefix } from './codemods/prefix' -import { simpleLegacyClasses } from './codemods/simple-legacy-classes' -import { themeToVar } from './codemods/theme-to-var' -import { variantOrder } from './codemods/variant-order' -import { spliceChangesIntoString, type StringChange } from './splice-changes-into-string' - -export type Migration = ( - designSystem: DesignSystem, - userConfig: Config, - rawCandidate: string, - location?: { - contents: string - start: number - end: number - }, -) => string | Promise - -export const DEFAULT_MIGRATIONS: Migration[] = [ - handleEmptyArbitraryValues, - prefix, - important, - bgGradient, - simpleLegacyClasses, - legacyClasses, - maxWidthScreen, - themeToVar, - variantOrder, // Has to happen before migrations that modify variants - automaticVarInjection, - legacyArbitraryValues, - arbitraryValueToBareValue, - modernizeArbitraryValues, -] - -export async function migrateCandidate( - designSystem: DesignSystem, - userConfig: Config, - rawCandidate: string, - // Location is only set when migrating a candidate from a source file - location?: { - contents: string - start: number - end: number - }, -): Promise { - for (let migration of DEFAULT_MIGRATIONS) { - rawCandidate = await migration(designSystem, userConfig, rawCandidate, location) - } - return rawCandidate -} - -export default async function migrateContents( - designSystem: DesignSystem, - userConfig: Config, - contents: string, - extension: string, -): Promise { - let candidates = await extractRawCandidates(contents, extension) - - let changes: StringChange[] = [] - - for (let { rawCandidate, start, end } of candidates) { - let migratedCandidate = await migrateCandidate(designSystem, userConfig, rawCandidate, { - contents, - start, - end, - }) - - if (migratedCandidate === rawCandidate) { - continue - } - - changes.push({ - start, - end, - replacement: migratedCandidate, - }) - } - - return spliceChangesIntoString(contents, changes) -} - -export async function migrate(designSystem: DesignSystem, userConfig: Config, file: string) { - let fullPath = path.isAbsolute(file) ? file : path.resolve(process.cwd(), file) - let contents = await fs.readFile(fullPath, 'utf-8') - - await fs.writeFile( - fullPath, - await migrateContents(designSystem, userConfig, contents, extname(file)), - ) -} diff --git a/packages/@tailwindcss-upgrade/src/utils/git.ts b/packages/@tailwindcss-upgrade/src/utils/git.ts index d716fbb8a..38edf21cb 100644 --- a/packages/@tailwindcss-upgrade/src/utils/git.ts +++ b/packages/@tailwindcss-upgrade/src/utils/git.ts @@ -1,8 +1,20 @@ import { execSync } from 'node:child_process' -export function isRepoDirty() { +export function gitRoot(cwd?: string): string | null { try { - let stdout = execSync('git status --porcelain', { encoding: 'utf-8' }) + return execSync('git rev-parse --show-toplevel', { + encoding: 'utf-8', + cwd, + stdio: ['ignore', 'pipe', 'ignore'], + }).trim() + } catch { + return null + } +} + +export function isRepoDirty(cwd?: string) { + try { + let stdout = execSync('git status --porcelain', { encoding: 'utf-8', cwd }) return stdout.trim() !== '' } catch (error) { // If it's not a git repository we don't know if it's dirty or not. But we diff --git a/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.test.ts b/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.test.ts deleted file mode 100644 index 1e6d8b1e8..000000000 --- a/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { expect, it } from 'vitest' -import { hoistStaticGlobParts } from './hoist-static-glob-parts' - -it.each([ - // A basic glob - [ - { base: '/projects/project-a', pattern: './src/**/*.html' }, - [{ base: '/projects/project-a/src', pattern: '**/*.html' }], - ], - - // A glob pointing to a folder should result in `**/*` - [ - { base: '/projects/project-a', pattern: './src' }, - [{ base: '/projects/project-a/src', pattern: '**/*' }], - ], - - // A glob pointing to a file, should result in the file as the pattern - [ - { base: '/projects/project-a', pattern: './src/index.html' }, - [{ base: '/projects/project-a/src', pattern: 'index.html' }], - ], - - // A glob going up a directory, should result in the new directory as the base - [ - { base: '/projects/project-a', pattern: '../project-b/src/**/*.html' }, - [{ base: '/projects/project-b/src', pattern: '**/*.html' }], - ], - - // A glob with curlies, should be expanded to multiple globs - [ - { base: '/projects/project-a', pattern: '../project-{b,c}/src/**/*.html' }, - [ - { base: '/projects/project-b/src', pattern: '**/*.html' }, - { base: '/projects/project-c/src', pattern: '**/*.html' }, - ], - ], - [ - { base: '/projects/project-a', pattern: '../project-{b,c}/src/**/*.{js,html}' }, - [ - { base: '/projects/project-b/src', pattern: '**/*.js' }, - { base: '/projects/project-b/src', pattern: '**/*.html' }, - { base: '/projects/project-c/src', pattern: '**/*.js' }, - { base: '/projects/project-c/src', pattern: '**/*.html' }, - ], - ], -])('should hoist the static parts of the glob: %s', (input, output) => { - expect(hoistStaticGlobParts(input)).toEqual(output) -}) diff --git a/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.ts b/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.ts deleted file mode 100644 index 18d587998..000000000 --- a/packages/@tailwindcss-upgrade/src/utils/hoist-static-glob-parts.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { normalizePath } from '@tailwindcss/node' -import braces from 'braces' -import path from 'node:path' - -interface GlobEntry { - base: string - pattern: string -} - -export function hoistStaticGlobParts(entry: GlobEntry): GlobEntry[] { - return braces(entry.pattern, { expand: true }).map((pattern) => { - let clone = { ...entry } - let [staticPart, dynamicPart] = splitPattern(pattern) - - // Move static part into the `base`. - let absolutePosixPath = normalizePath(entry.base) - - if (staticPart !== null) { - clone.base = path.posix.join(absolutePosixPath, staticPart) - } else { - clone.base = absolutePosixPath - } - - // Move dynamic part into the `pattern`. - if (dynamicPart === null) { - clone.pattern = '**/*' - } else { - clone.pattern = dynamicPart - } - - // If the pattern looks like a file, move the file name from the `base` to - // the `pattern`. - let file = path.basename(clone.base) - if (file.includes('.')) { - clone.pattern = file - clone.base = path.dirname(clone.base) - } - - return clone - }) -} - -// Split a glob pattern into a `static` and `dynamic` part. -// -// Assumption: we assume that all globs are expanded, which means that the only -// dynamic parts are using `*`. -// -// E.g.: -// Original input: `../project-b/**/*.{html,js}` -// Expanded input: `../project-b/**/*.html` & `../project-b/**/*.js` -// Split on first input: ("../project-b", "**/*.html") -// Split on second input: ("../project-b", "**/*.js") -function splitPattern(pattern: string): [staticPart: string | null, dynamicPart: string | null] { - // No dynamic parts, so we can just return the input as-is. - if (!pattern.includes('*')) { - return [pattern, null] - } - - let lastSlashPosition: number | null = null - - for (let i = 0; i < pattern.length; i++) { - let c = pattern[i] - if (c === '/') { - lastSlashPosition = i - } - - if (c === '*' || c === '!') { - break - } - } - - // Very first character is a `*`, therefore there is no static part, only a - // dynamic part. - if (lastSlashPosition === null) { - return [null, pattern] - } - - let staticPart = pattern.slice(0, lastSlashPosition).trim() - let dynamicPart = pattern.slice(lastSlashPosition + 1).trim() - - return [staticPart || null, dynamicPart || null] -} diff --git a/packages/@tailwindcss-upgrade/src/utils/package-version.ts b/packages/@tailwindcss-upgrade/src/utils/package-version.ts index 833ccadf2..64a1e7ed1 100644 --- a/packages/@tailwindcss-upgrade/src/utils/package-version.ts +++ b/packages/@tailwindcss-upgrade/src/utils/package-version.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'node:fs' import fs from 'node:fs/promises' import { resolveJsId } from './resolve' @@ -15,3 +16,14 @@ export async function getPackageVersion(pkg: string, base: string): Promise = { bun: '-d', } +const manifests = new DefaultMap((base) => { + try { + let packageJsonPath = resolve(base, 'package.json') + return readFileSync(packageJsonPath, 'utf-8') + } catch { + return '' + } +}) + export function pkg(base: string) { return { + async manager() { + return await packageManagerForBase.get(base) + }, async add(packages: string[], location: 'dependencies' | 'devDependencies' = 'dependencies') { let packageManager = await packageManagerForBase.get(base) let args = packages.slice() if (location === 'devDependencies') { args.push(SAVE_DEV[packageManager] || SAVE_DEV.default) } - return exec(`${packageManager} add ${args.join(' ')}`, { cwd: base }) + + // Allow running the `pnpm` command in the workspace root without + // erroring. Can't just use `--workspace-root` because that will force + // install dependencies in the workspace root. + if (packageManager === 'pnpm') { + args.push('--ignore-workspace-root-check') + } + + let command = `${packageManager} add ${args.join(' ')}` + try { + return await exec(command, { cwd: base }) + } catch (e: any) { + error(`An error occurred while running \`${command}\`\n\n${e.stdout}\n${e.stderr}`, { + prefix: '↳ ', + }) + throw e + } finally { + manifests.delete(base) + } + }, + has(name: string) { + return manifests.get(base).includes(`"${name}":`) }, async remove(packages: string[]) { let packageManager = await packageManagerForBase.get(base) - return exec(`${packageManager} remove ${packages.join(' ')}`, { cwd: base }) + let command = `${packageManager} remove ${packages.join(' ')}` + try { + return await exec(command, { cwd: base }) + } catch (e: any) { + error(`An error occurred while running \`${command}\`\n\n${e.stdout}\n${e.stderr}`, { + prefix: '↳ ', + }) + throw e + } finally { + manifests.delete(base) + } }, } } diff --git a/packages/@tailwindcss-upgrade/src/utils/renderer.ts b/packages/@tailwindcss-upgrade/src/utils/renderer.ts index 38bd9826a..c9a967801 100644 --- a/packages/@tailwindcss-upgrade/src/utils/renderer.ts +++ b/packages/@tailwindcss-upgrade/src/utils/renderer.ts @@ -44,7 +44,7 @@ export function wordWrap(text: string, width: number): string[] { // Handle text with newlines by maintaining the newlines, then splitting // each line separately. if (text.includes('\n')) { - return text.split('\n').flatMap((line) => wordWrap(line, width)) + return text.split('\n').flatMap((line) => (line ? wordWrap(line, width) : [''])) } let words = text.split(' ') diff --git a/packages/@tailwindcss-upgrade/src/template/splice-changes-into-string.ts b/packages/@tailwindcss-upgrade/src/utils/splice-changes-into-string.ts similarity index 100% rename from packages/@tailwindcss-upgrade/src/template/splice-changes-into-string.ts rename to packages/@tailwindcss-upgrade/src/utils/splice-changes-into-string.ts diff --git a/packages/@tailwindcss-upgrade/src/utils/version.ts b/packages/@tailwindcss-upgrade/src/utils/version.ts new file mode 100644 index 000000000..27bc44f2b --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/utils/version.ts @@ -0,0 +1,63 @@ +import { execSync } from 'node:child_process' +import semver from 'semver' +import { DefaultMap } from '../../../tailwindcss/src/utils/default-map' +import { getPackageVersionSync } from './package-version' + +/** + * Must be of major version. + * + * E.g.: `isMajor(3)` + */ +export function isMajor(version: number) { + return semver.satisfies(installedTailwindVersion(), `>=${version}.0.0 <${version + 1}.0.0`) +} + +/** + * Must be of greater than the current major version including minor and patch. + * + * E.g.: `isGreaterThan(3)` + */ +export function isGreaterThan(version: number) { + return semver.gte(installedTailwindVersion(), `${version + 1}.0.0`) +} + +let cache = new DefaultMap((base) => { + let tailwindVersion = getPackageVersionSync('tailwindcss', base) + if (!tailwindVersion) throw new Error('Tailwind CSS is not installed') + return tailwindVersion +}) + +export function installedTailwindVersion(base = process.cwd()): string { + return cache.get(base) +} + +let expectedCache = new DefaultMap((base) => { + try { + // This will report a problem if the package.json/package-lock.json + // mismatches with the installed version in node_modules. + // + // Also tested this with Bun and PNPM, both seem to work fine. + execSync('npm ls tailwindcss --json', { cwd: base, stdio: 'pipe' }) + return installedTailwindVersion(base) + } catch (_e) { + try { + let e = _e as { stdout: Buffer } + let data = JSON.parse(e.stdout.toString()) + + return ( + // Could be a sub-dependency issue, but we are only interested in + // the top-level version mismatch. + /"(.*?)" from the root project/.exec(data.dependencies.tailwindcss.invalid)?.[1] ?? + // Fallback to the installed version + installedTailwindVersion(base) + ) + } catch { + // We don't know how to verify, so let's just return the installed + // version to not block the user. + return installedTailwindVersion(base) + } + } +}) +export function expectedTailwindVersion(base = process.cwd()): string { + return expectedCache.get(base) +} diff --git a/packages/@tailwindcss-upgrade/src/utils/walk-variants.ts b/packages/@tailwindcss-upgrade/src/utils/walk-variants.ts new file mode 100644 index 000000000..e0dd16899 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/utils/walk-variants.ts @@ -0,0 +1,18 @@ +import type { Candidate, Variant } from '../../../tailwindcss/src/candidate' + +export function* walkVariants(candidate: Candidate) { + function* inner( + variant: Variant, + parent: Extract | null = null, + ): Iterable<[Variant, Extract | null]> { + yield [variant, parent] + + if (variant.kind === 'compound') { + yield* inner(variant.variant, variant) + } + } + + for (let variant of candidate.variants) { + yield* inner(variant, null) + } +} diff --git a/packages/@tailwindcss-upgrade/src/utils/write-file-safely.ts b/packages/@tailwindcss-upgrade/src/utils/write-file-safely.ts new file mode 100644 index 000000000..2ff99f9e7 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/utils/write-file-safely.ts @@ -0,0 +1,49 @@ +import { randomUUID } from 'node:crypto' +import fs from 'node:fs/promises' +import path from 'node:path' + +export async function writeFileSafely(file: string, contents: string) { + // Resolve symlinks so the rename replaces the actual target file rather than + // replacing the symlink itself with a regular file. + let realFile = await fs.realpath(file).catch(() => file) + + // Start by creating a new file in the current directory that is guaranteed to + // be unique (via `uuid`). We can embed the `process.id` in case we need to + // debug things later. + // + // While we can write this to a more global `/tmp` folder, I want to be 100% + // sure that we are on the same file system (same drive) so the rename + // operation is atomic. Once the file is written, we will rename the file. If + // this fails, the old file is still intact, if it works we have an updated + // file. + // + // If this still causes problems (but it will slow things down): + // 1. We could make sure that we inherit the file permissions + // 2. Use an explicit fsync to force a flush to disk + let temporaryFile = path.join( + path.dirname(realFile), + `.${path.basename(realFile)}.tailwind-upgrade.${process.pid}.${randomUUID()}.tmp`, + ) + + // Write file uses the `w` flag by default, which is defined as: + // > Open file for writing. The file is created (if it does not exist) or truncated (if it exists). + // > https://nodejs.org/api/fs.html#file-system-flags + // + // Which means that if this function is actively running, and you cancel the + // process at the wrong time, then the truncated files are present. Since all + // these migrations happen in parallel, multiple files are open and available + // to be written to, it could mean in multiple truncated files. + // + // Writing to a temp file first means that if the process is cancelled at this + // point, that the old original file is still correct. + // + // The rename part should be atomic (especially because we guarantee it to be + // on the same file system) so this either succeeds or doesn't happen. + try { + await fs.writeFile(temporaryFile, contents, 'utf8') + await fs.rename(temporaryFile, realFile) + } catch (error) { + await fs.unlink(temporaryFile).catch(() => {}) + throw error + } +} diff --git a/packages/@tailwindcss-vite/README.md b/packages/@tailwindcss-vite/README.md index 95ec9d87d..1a5d25c73 100644 --- a/packages/@tailwindcss-vite/README.md +++ b/packages/@tailwindcss-vite/README.md @@ -13,10 +13,10 @@

- Build Status + Build Status Total Downloads - Latest Release - License + Latest Release + License

--- @@ -27,14 +27,50 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com). ## Community -For help, discussion about best practices, or any other conversation that would benefit from being searchable: +For help, discussion about best practices, or feature ideas: -[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) - -For chatting with others using the framework: - -[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe) +[Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions) ## Contributing -If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. +If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**. + +--- + +## `@tailwindcss/vite` plugin API + +### Enabling or disabling Lightning CSS + +By default, this plugin detects whether or not the CSS is being built for production by checking the `NODE_ENV` environment variable. When building for production Lightning CSS will be enabled otherwise it is disabled. + +If you want to always enable or disable Lightning CSS the `optimize` option may be used: + +```js +import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [ + tailwindcss({ + // Disable Lightning CSS optimization + optimize: false, + }), + ], +}) +``` + +It's also possible to keep Lightning CSS enabled but disable minification: + +```js +import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [ + tailwindcss({ + // Enable Lightning CSS but disable minification + optimize: { minify: false }, + }), + ], +}) +``` diff --git a/packages/@tailwindcss-vite/package.json b/packages/@tailwindcss-vite/package.json index 5c50f8328..821704a1d 100644 --- a/packages/@tailwindcss-vite/package.json +++ b/packages/@tailwindcss-vite/package.json @@ -1,6 +1,6 @@ { "name": "@tailwindcss/vite", - "version": "4.0.0-beta.7", + "version": "4.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "repository": { @@ -24,13 +24,12 @@ "exports": { ".": { "types": "./dist/index.d.mts", - "import": "./dist/index.mjs" + "default": "./dist/index.mjs" } }, "dependencies": { - "@tailwindcss/node": "workspace:^", - "@tailwindcss/oxide": "workspace:^", - "lightningcss": "catalog:", + "@tailwindcss/node": "workspace:*", + "@tailwindcss/oxide": "workspace:*", "tailwindcss": "workspace:*" }, "devDependencies": { @@ -38,6 +37,6 @@ "vite": "catalog:" }, "peerDependencies": { - "vite": "^5.2.0 || ^6" + "vite": "^5.2.0 || ^6 || ^7 || ^8" } } diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index b3c9ace1e..8fc660eaa 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -1,213 +1,203 @@ -import { compile, env, Features, Instrumentation, normalizePath } from '@tailwindcss/node' +import { + compile, + env, + Features, + Instrumentation, + normalizePath, + optimize, + toSourceMap, +} from '@tailwindcss/node' import { clearRequireCache } from '@tailwindcss/node/require-cache' import { Scanner } from '@tailwindcss/oxide' -import { Features as LightningCssFeatures, transform } from 'lightningcss' import fs from 'node:fs/promises' import path from 'node:path' -import type { Plugin, ResolvedConfig, Rollup, Update, ViteDevServer } from 'vite' +import type { Environment, InternalResolveOptions, Plugin, ResolvedConfig } from 'vite' +import * as vite from 'vite' const DEBUG = env.DEBUG -const SPECIAL_QUERY_RE = /[?&](raw|url)\b/ +const SPECIAL_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)\b/ +const COMMON_JS_PROXY_RE = /\?commonjs-proxy/ +const INLINE_STYLE_ID_RE = /[?&]index=\d+\.css$/ -export default function tailwindcss(): Plugin[] { - let servers: ViteDevServer[] = [] +export type PluginOptions = { + /** + * Optimize and minify the output CSS. + */ + optimize?: boolean | { minify?: boolean } +} + +function createCustomResolver( + resolvers: ((id: string, importer: string) => Promise)[], + filter = (_path: string) => true, +) { + return async (id: string, base: string) => { + // The resolver expects an `importer` file. We don't really know where the + // current `id` was imported from, but Vite will essentially do a + // `path.dirname(importer)` so it doesn't really matter. + // + // It does matter that this is a file, otherwise we would go up a directory, + // which means that we would be resolving files from a parent folder first, + // instead of the current folder we are in. + let importer = path.resolve(base, '__placeholder__.ts') + + for (let resolver of resolvers) { + let resolved = await resolver(id, importer) + + // If we didn't resolve, we don't have to bail immediately, but we can try + // the next resolver + if (!resolved) continue + + if (resolved === id) continue + + // Looks like a relative file, let's resolve it to an absolute path + if (resolved[0] === '.') resolved = path.resolve(base, resolved) + + // Must adhere to additional filters (e.g.: must be a .css file) + if (!filter(resolved)) continue + + // If it's not an absolute path, then we don't really know how to read + // the file from disk. + if (!path.isAbsolute(resolved)) continue + + return resolved + } + } +} + +export default function tailwindcss(opts: PluginOptions = {}): Plugin[] { let config: ResolvedConfig | null = null + let rootsByEnv = new DefaultMap>((env: string) => new Map()) let isSSR = false - let minify = false + let shouldOptimize = true + let minify = true - // The Vite extension has two types of sources for candidates: - // - // 1. The module graph: These are all modules that vite transforms and we want - // them to be automatically scanned for candidates. - // 2. Root defined `@source`s - // - // Module graph candidates are global to the Vite extension since we do not - // know which CSS roots will be used for the modules. We are using a custom - // scanner instance with auto source discovery disabled to parse these. - // - // For candidates coming from custom `@source` directives of the CSS roots, we - // create an individual scanner for each root. - // - // Note: To improve performance, we do not remove candidates from this set. - // This means a longer-ongoing dev mode session might contain candidates that - // are no longer referenced in code. - let moduleGraphCandidates = new DefaultMap>(() => new Set()) - let moduleGraphScanner = new Scanner({}) + function createRoot(env: Environment | null, id: string) { + type ResolveFn = (id: string, base: string) => Promise - let roots: DefaultMap = new DefaultMap((id) => { - let cssResolver = config!.createResolver({ - ...config!.resolve, - extensions: ['.css'], - mainFields: ['style'], - conditions: ['style', 'development|production'], - tryIndex: false, - preferRelative: true, - }) - function customCssResolver(id: string, base: string) { - return cssResolver(id, base, true, isSSR) + let customCssResolver: ResolveFn + let customJsResolver: ResolveFn + + if (!env) { + // Older, pre-environment Vite API + // TODO: Can we drop this?? + let cssResolver = config!.createResolver({ + ...config!.resolve, + extensions: ['.css'], + mainFields: ['style'], + conditions: ['style', 'development|production'], + tryIndex: false, + preferRelative: true, + }) + + let jsResolver = config!.createResolver(config!.resolve) + + customCssResolver = createCustomResolver( + [ + (id, importer) => cssResolver(id, importer, true, isSSR), + (id, importer) => cssResolver(id, importer, false, isSSR), + ], + (path) => path.endsWith('.css'), + ) + customJsResolver = createCustomResolver( + [ + (id, importer) => jsResolver(id, importer, true, isSSR), + (id, importer) => jsResolver(id, importer, false, isSSR), + ], + (path) => !path.endsWith('.css'), + ) + } else { + type ResolveIdFn = ( + environment: Environment, + id: string, + importer?: string, + aliasOnly?: boolean, + ) => Promise + + // There are cases where Environment API is available, + // but `createResolver` is still overridden (for example astro v5) + // + // Copied as-is from vite, because this function is not a part of public API + // + // TODO: Remove this function and pre-environment code when Vite < 7 is no longer supported + function createBackCompatIdResolver( + config: ResolvedConfig, + options?: Partial, + ): ResolveIdFn { + const compatResolve = config.createResolver(options) + let resolve: ResolveIdFn + return async (environment, id, importer, aliasOnly) => { + if (environment.name === 'client' || environment.name === 'ssr') { + return compatResolve(id, importer, aliasOnly, environment.name === 'ssr') + } + resolve ??= vite.createIdResolver(config, options) + return resolve(environment, id, importer, aliasOnly) + } + } + + // Newer Vite versions + let cssResolver = createBackCompatIdResolver(env.config, { + ...env.config.resolve, + extensions: ['.css'], + mainFields: ['style'], + conditions: ['style', 'development|production'], + tryIndex: false, + preferRelative: true, + }) + + let jsResolver = createBackCompatIdResolver(env.config, env.config.resolve) + + customCssResolver = createCustomResolver( + [ + (id, importer) => cssResolver(env, id, importer, true), + (id, importer) => cssResolver(env, id, importer, false), + ], + (path) => path.endsWith('.css'), + ) + customJsResolver = createCustomResolver( + [ + (id, importer) => jsResolver(env, id, importer, true), + (id, importer) => jsResolver(env, id, importer, false), + ], + (path) => !path.endsWith('.css'), + ) } - let jsResolver = config!.createResolver(config!.resolve) - function customJsResolver(id: string, base: string) { - return jsResolver(id, base, true, isSSR) - } return new Root( id, - () => moduleGraphCandidates, - config!.base, + config!.root, + // Currently, Vite only supports CSS source maps in development and they + // are off by default. Check to see if we need them or not. + config?.css.devSourcemap ?? false, customCssResolver, customJsResolver, ) - }) - - function scanFile(id: string, content: string, extension: string, isSSR: boolean) { - let updated = false - for (let candidate of moduleGraphScanner.scanFiles([{ content, extension }])) { - updated = true - moduleGraphCandidates.get(id).add(candidate) - } - - if (updated) { - invalidateAllRoots(isSSR) - } - } - - function invalidateAllRoots(isSSR: boolean) { - for (let server of servers) { - let updates: Update[] = [] - for (let [id, root] of roots.entries()) { - let module = server.moduleGraph.getModuleById(id) - if (!module) { - // The module for this root might not exist yet - if (root.builtBeforeTransform) { - continue - } - - // Note: Removing this during SSR is not safe and will produce - // inconsistent results based on the timing of the removal and - // the order / timing of transforms. - if (!isSSR) { - // It is safe to remove the item here since we're iterating on a copy - // of the keys. - roots.delete(id) - } - continue - } - - roots.get(id).requiresRebuild = false - server.moduleGraph.invalidateModule(module) - updates.push({ - type: `${module.type}-update`, - path: module.url, - acceptedPath: module.url, - timestamp: Date.now(), - }) - } - - if (updates.length > 0) { - server.hot.send({ type: 'update', updates }) - } - } - } - - async function regenerateOptimizedCss( - root: Root, - addWatchFile: (file: string) => void, - I: Instrumentation, - ) { - let content = root.lastContent - let generated = await root.generate(content, addWatchFile, I) - if (generated === false) { - return - } - DEBUG && I.start('Optimize CSS') - let result = optimizeCss(generated, { minify }) - DEBUG && I.end('Optimize CSS') - return result - } - - // Manually run the transform functions of non-Tailwind plugins on the given CSS - async function transformWithPlugins(context: Rollup.PluginContext, id: string, css: string) { - let transformPluginContext = { - ...context, - getCombinedSourcemap: () => { - throw new Error('getCombinedSourcemap not implemented') - }, - } - - for (let plugin of config!.plugins) { - if (!plugin.transform) continue - - if (plugin.name.startsWith('@tailwindcss/')) { - // We do not run any Tailwind transforms anymore - continue - } else if ( - plugin.name.startsWith('vite:') && - // Apply the vite:css plugin to generated CSS for transformations like - // URL path rewriting and image inlining. - plugin.name !== 'vite:css' && - // In build mode, since `renderStart` runs after all transformations, we - // need to also apply vite:css-post. - plugin.name !== 'vite:css-post' && - // The vite:vue plugin handles CSS specific post-processing for Vue - plugin.name !== 'vite:vue' - ) { - continue - } else if (plugin.name === 'ssr-styles') { - // The Nuxt ssr-styles plugin emits styles from server-side rendered - // components, we can't run it in the `renderStart` phase so we're - // skipping it. - continue - } - - let transformHandler = - 'handler' in plugin.transform! ? plugin.transform.handler : plugin.transform! - - try { - // Directly call the plugin's transform function to process the - // generated CSS. In build mode, this updates the chunks later used to - // generate the bundle. In serve mode, the transformed source should be - // applied in transform. - let result = await transformHandler.call(transformPluginContext, css, id) - if (!result) continue - if (typeof result === 'string') { - css = result - } else if (result.code) { - css = result.code - } - } catch (e) { - console.error(`Error running ${plugin.name} on Tailwind CSS output. Skipping.`) - } - } - return css } return [ - svelteProcessor(roots), { // Step 1: Scan source files for candidates name: '@tailwindcss/vite:scan', enforce: 'pre', - configureServer(server) { - servers.push(server) - }, - async configResolved(_config) { config = _config - minify = config.build.cssMinify !== false isSSR = config.build.ssr !== false && config.build.ssr !== undefined - }, - // Scan all non-CSS files for candidates - transformIndexHtml(html, { path }) { - scanFile(path, html, 'html', isSSR) - }, - transform(src, id, options) { - let extension = getExtension(id) - if (isPotentialCssRootFile(id)) return - scanFile(id, src, extension, options?.ssr ?? false) + // By default we optimize CSS during the build phase but if the user + // provides explicit options we'll use those instead + if (opts.optimize !== undefined) { + shouldOptimize = opts.optimize !== false + } + + // Minification is also performed when optimizing as long as it's also + // enabled in Vite + minify = shouldOptimize && config.build.cssMinify !== false + + // But again, the user can override that choice explicitly + if (typeof opts.optimize === 'object') { + minify = opts.optimize.minify !== false + } }, }, @@ -216,45 +206,35 @@ export default function tailwindcss(): Plugin[] { name: '@tailwindcss/vite:generate:serve', apply: 'serve', enforce: 'pre', + transform: { + filter: { + id: { + exclude: [/\/\.vite\//, SPECIAL_QUERY_RE, COMMON_JS_PROXY_RE], + include: [/\.css(?:\?.*)?$/, /&lang\.css/, INLINE_STYLE_ID_RE], + }, + }, + async handler(src, id) { + if (!isPotentialCssRootFile(id)) return - async transform(src, id, options) { - if (!isPotentialCssRootFile(id)) return + using I = new Instrumentation() + DEBUG && I.start('[@tailwindcss/vite] Generate CSS (serve)') - using I = new Instrumentation() - I.start('[@tailwindcss/vite] Generate CSS (serve)') + let roots = rootsByEnv.get(this.environment?.name ?? 'default') + let root = roots.get(id) + if (!root) { + root ??= createRoot(this.environment ?? null, id) + roots.set(id, root) + } - let root = roots.get(id) + let result = await root.generate(src, (file) => this.addWatchFile(file), I) + if (!result) { + roots.delete(id) + return + } - // If the root was built outside of the transform hook (e.g. in the - // Svelte preprocessor), we still want to mark all dependencies of the - // root as watched files. - if (root.builtBeforeTransform) { - root.builtBeforeTransform.forEach((file) => this.addWatchFile(file)) - root.builtBeforeTransform = undefined - } - - // We only process Svelte `' + + '
' + + '
one
' + + '
two
' + + '
' + } + } + + customElements.define('my-component', Component) + + + + `, + ) + + let gap = await page.evaluate(() => { + let shadowRoot = document.querySelector('#shadow')!.shadowRoot! + let x = shadowRoot.querySelector('#x')! + return window.getComputedStyle(x).getPropertyValue('gap') + }) + + expect(gap).toBe('8px') +}) + +// @scope related tests +{ + test('custom variant using `@scope`', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
+
In scope
+
+
Beyond the limit
+
+
+
Not in scope
+ `, + css` + @custom-variant in-checkout { + @scope (.checkout) to (.payment) { + @slot; + } + } + `, + ) + + test.skip(!(await supportsAtScope(page)), '`@scope` is not supported in this browser') + + expect(await getPropertyValue('#in', 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue('#beyond', 'text-decoration-line')).toEqual('none') + expect(await getPropertyValue('#out', 'text-decoration-line')).toEqual('none') + }) + + test('custom variant using `@scope` behaves like the equivalent authored CSS', async ({ + page, + }) => { + // Render the same structure twice: + // - Once styled by authored CSS using `@scope`, like the "normal CSS" + // reference in https://github.com/tailwindlabs/tailwindcss/issues/18961 + // - Once styled by utilities using an equivalent custom variant + function structure(prefix: string, probe: string) { + return html` +
+
In scope
+
+
Beyond the limit
+
+
+
Not in scope
+ ` + } + + let { getPropertyValue } = await render( + page, + structure('a', 'a-probe') + structure('c', 'in-checkout:underline'), + css` + @custom-variant in-checkout { + @scope (.checkout) to (.payment) { + @slot; + } + } + `, + ) + + // The `a-*` structure is styled by the authored CSS as-is + await page.addStyleTag({ + content: css` + @scope (.checkout) to (.payment) { + .a-probe { + text-decoration-line: underline; + } + } + `, + }) + + test.skip(!(await supportsAtScope(page)), '`@scope` is not supported in this browser') + + for (let prefix of ['a', 'c']) { + expect(await getPropertyValue(`#${prefix}-in`, 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue(`#${prefix}-beyond`, 'text-decoration-line')).toEqual('none') + expect(await getPropertyValue(`#${prefix}-out`, 'text-decoration-line')).toEqual('none') + } + }) + + test('user-authored nested `@scope` behaves like the authored CSS', async ({ page }) => { + // User-authored `@scope` rules are compiled to their native CSS nesting + // semantics. Rendering the same structure twice: + // - Once styled by the authored (nested) CSS as-is + // - Once styled by the compiled output + function structure(prefix: string) { + return html` +
+
+ in scope +
+ beyond the limit +
+
+
+
+ scope not inside parent +
+ ` + } + + function scopeCss(prefix: string) { + return css` + .${prefix}-parent { + @scope (& > .${prefix}-scope) to (& .${prefix}-limit) { + .${prefix}-content { + text-decoration-line: underline; + } + } + } + ` + } + + // The `c-*` structure is styled by the compiled CSS + let { getPropertyValue } = await render(page, structure('a') + structure('c'), scopeCss('c')) + + // The `a-*` structure is styled by the authored CSS as-is + await page.addStyleTag({ content: scopeCss('a') }) + + test.skip(!(await supportsAtScope(page)), '`@scope` is not supported in this browser') + + // The compiled output only applies within the scope + expect(await getPropertyValue('#c-in', 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue('#c-beyond', 'text-decoration-line')).toEqual('none') + expect(await getPropertyValue('#c-out', 'text-decoration-line')).toEqual('none') + + // The authored CSS behaves the same + expect(await getPropertyValue('#a-in', 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue('#a-out', 'text-decoration-line')).toEqual('none') + + // WebKit currently drops the `` limit entirely when it contains + // `&`. Since we resolve the `&` at build time, the compiled output works + // even though the authored CSS doesn't. + if (test.info().project.name !== 'webkit') { + expect(await getPropertyValue('#a-beyond', 'text-decoration-line')).toEqual('none') + } + }) + + test('user-authored `@scope` nested inside another `@scope`', async ({ page }) => { + function structure(prefix: string) { + return html` + +
+
+
ctrl
+
+
+ + +
+
+
ancestor
+
+
+ + +
+
+
+
beyond
+
+
+
+ ` + } + + function scopeCss(prefix: string) { + return css` + @scope (.${prefix}-outer) to (.${prefix}-outer-limit) { + .${prefix}-middle { + @scope (.${prefix}-inner) to (.${prefix}-inner-limit) { + text-decoration-line: underline; + } + } + } + ` + } + + // The `c-*` structure is styled by the compiled CSS + let { getPropertyValue } = await render(page, structure('a') + structure('c'), scopeCss('c')) + + // The `a-*` structure is styled by the authored CSS as-is + await page.addStyleTag({ content: scopeCss('a') }) + + test.skip(!(await supportsAtScope(page)), '`@scope` is not supported in this browser') + + // The compiled output behaves the same in every browser + expect(await getPropertyValue('#c-ctrl', 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue('#c-ancestor', 'text-decoration-line')).toEqual('none') + expect(await getPropertyValue('#c-beyond', 'text-decoration-line')).toEqual('none') + + // The authored CSS behaves the same … + expect(await getPropertyValue('#a-ctrl', 'text-decoration-line')).toEqual('underline') + expect(await getPropertyValue('#a-beyond', 'text-decoration-line')).toEqual('none') + + // … except in Chromium, which resolves the nesting context of a + // rule-nested `@scope` without the enclosing scope's constraint (Safari + // and Firefox both constrain it). The compiled output normalizes this + // browser difference. + if (test.info().project.name !== 'chromium') { + expect(await getPropertyValue('#a-ancestor', 'text-decoration-line')).toEqual('none') + } + }) + + function supportsAtScope(page: Page) { + return page.evaluate(() => typeof CSSScopeRule !== 'undefined') + } +} + +for (let kind of ['group', 'peer']) { + test(`${kind}-* variants`, async ({ page }) => { + let targets = html` +
+
+ ` + + let { getPropertyValue } = await render( + page, + kind === 'group' + ? html`
${targets}
` + : html`
+
+ ${targets} +
`, + css` + .owner > .applied { + @apply ${kind}-focus:z-20; + } + `, + ) + + expect(await getPropertyValue('#target', 'z-index')).toEqual('auto') + expect(await getPropertyValue('#applied', 'z-index')).toEqual('auto') + + await page.locator('#trigger').focus() + + expect(await getPropertyValue('#target', 'z-index')).toEqual('10') + expect(await getPropertyValue('#applied', 'z-index')).toEqual('20') + + await page.locator('#trigger').blur() + + expect(await getPropertyValue('#target', 'z-index')).toEqual('auto') + expect(await getPropertyValue('#applied', 'z-index')).toEqual('auto') + }) +} + +test('group-* variants do not change specificity', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
+
+
+ `, + css` + @layer utilities { + .target { + z-index: 20; + } + .target.equal { + z-index: 30; + } + } + `, + ) + + await page.locator('#trigger').focus() + + expect(await getPropertyValue('#target', 'z-index')).toEqual('10') + + await page.locator('#target').evaluate((element) => element.classList.add('equal')) + + expect(await getPropertyValue('#target', 'z-index')).toEqual('30') +}) + +test('stacked group-* variants match regardless of the order of the groups', async ({ page }) => { + for (let [outer, inner] of [ + ['a', 'b'], + ['b', 'a'], + ]) { + let { getPropertyValue } = await render( + page, + html` +
+
+
+
+
+ `, + ) + + expect(await getPropertyValue('#target', 'z-index')).toEqual('10') + + await page.locator('#inner').evaluate((element) => element.removeAttribute('data-active')) + + expect(await getPropertyValue('#target', 'z-index')).toEqual('auto') + } +}) + +test('compound group-* and peer-* variants', async ({ page }) => { + let { getPropertyValue } = await render( + page, + html` +
+
+
+
+
+
+ `, + ) + + await page.locator('#peer').focus() + + expect(await getPropertyValue('#target', 'z-index')).toEqual('10') + expect(await getPropertyValue('#middle', 'z-index')).toEqual('auto') + + await page.locator('#outer').focus() + + expect(await getPropertyValue('#target', 'z-index')).toEqual('auto') + expect(await getPropertyValue('#middle', 'z-index')).toEqual('20') +}) + // --- const preflight = fs.readFileSync(path.resolve(__dirname, '..', 'preflight.css'), 'utf-8') @@ -654,6 +2516,8 @@ async function render(page: Page, content: string, extraCss: string = '') { --color-red: rgb(255, 0, 0); --color-green: rgb(0, 255, 0); --color-blue: rgb(0, 0, 255); + --color-black: black; + --color-transparent: transparent; } } @layer base { @@ -673,14 +2537,13 @@ async function render(page: Page, content: string, extraCss: string = '') { // mouse to before running the tests. content = `
${content}` - await page.setContent(content) - let scanner = new Scanner({}) let candidates = scanner.scanFiles([{ content, extension: 'html' }]) - await page.addStyleTag({ - content: optimizeCss(build(candidates)), - }) + let { code: styles } = optimize(build(candidates)) + + content = `${content}` + await page.setContent(content) await page.locator('#mouse-park').hover() @@ -692,6 +2555,16 @@ async function render(page: Page, content: string, extraCss: string = '') { property, ) }, + + async getPropertyList(selector: string | [string, string], property: string) { + let value = await getPropertyValue( + page, + Array.isArray(selector) ? selector : [selector, undefined], + property, + ) + + return segment(value, ',').map((item) => item.trim()) + }, } } @@ -700,7 +2573,7 @@ async function getPropertyValue( selector: [string, string | undefined], property: string, ) { - return page.evaluate( + let value = await page.evaluate( ([[selector, pseudo], property]) => { return window .getComputedStyle(document.querySelector(selector)!, pseudo) @@ -708,4 +2581,16 @@ async function getPropertyValue( }, [selector, property] as const, ) + + switch (property) { + case 'outline': + // Order in webkit vs chromium is different. Sort to normalize. + return segment(value, ' ') + .map((part) => part.trim()) + .sort((a, z) => a.length - z.length) + .join(' ') + + default: + return value + } } diff --git a/packages/tailwindcss/theme.css b/packages/tailwindcss/theme.css index a0652e07d..05364f5c2 100644 --- a/packages/tailwindcss/theme.css +++ b/packages/tailwindcss/theme.css @@ -1,273 +1,323 @@ @theme default { - --font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', - 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-sans: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', Arial, + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; --font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', - 'Courier New', monospace; + --font-mono: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', + monospace; - --color-red-50: oklch(0.971 0.013 17.38); - --color-red-100: oklch(0.936 0.032 17.717); - --color-red-200: oklch(0.885 0.062 18.334); - --color-red-300: oklch(0.808 0.114 19.571); - --color-red-400: oklch(0.704 0.191 22.216); - --color-red-500: oklch(0.637 0.237 25.331); - --color-red-600: oklch(0.577 0.245 27.325); - --color-red-700: oklch(0.505 0.213 27.518); - --color-red-800: oklch(0.444 0.177 26.899); - --color-red-900: oklch(0.396 0.141 25.723); - --color-red-950: oklch(0.258 0.092 26.042); + --color-red-50: oklch(97.1% 0.013 17.38); + --color-red-100: oklch(93.6% 0.032 17.717); + --color-red-200: oklch(88.5% 0.062 18.334); + --color-red-300: oklch(80.8% 0.114 19.571); + --color-red-400: oklch(70.4% 0.191 22.216); + --color-red-500: oklch(63.7% 0.237 25.331); + --color-red-600: oklch(57.7% 0.245 27.325); + --color-red-700: oklch(50.5% 0.213 27.518); + --color-red-800: oklch(44.4% 0.177 26.899); + --color-red-900: oklch(39.6% 0.141 25.723); + --color-red-950: oklch(25.8% 0.092 26.042); - --color-orange-50: oklch(0.98 0.016 73.684); - --color-orange-100: oklch(0.954 0.038 75.164); - --color-orange-200: oklch(0.901 0.076 70.697); - --color-orange-300: oklch(0.837 0.128 66.29); - --color-orange-400: oklch(0.75 0.183 55.934); - --color-orange-500: oklch(0.705 0.213 47.604); - --color-orange-600: oklch(0.646 0.222 41.116); - --color-orange-700: oklch(0.553 0.195 38.402); - --color-orange-800: oklch(0.47 0.157 37.304); - --color-orange-900: oklch(0.408 0.123 38.172); - --color-orange-950: oklch(0.266 0.079 36.259); + --color-orange-50: oklch(98% 0.016 73.684); + --color-orange-100: oklch(95.4% 0.038 75.164); + --color-orange-200: oklch(90.1% 0.076 70.697); + --color-orange-300: oklch(83.7% 0.128 66.29); + --color-orange-400: oklch(75% 0.183 55.934); + --color-orange-500: oklch(70.5% 0.213 47.604); + --color-orange-600: oklch(64.6% 0.222 41.116); + --color-orange-700: oklch(55.3% 0.195 38.402); + --color-orange-800: oklch(47% 0.157 37.304); + --color-orange-900: oklch(40.8% 0.123 38.172); + --color-orange-950: oklch(26.6% 0.079 36.259); - --color-amber-50: oklch(0.987 0.022 95.277); - --color-amber-100: oklch(0.962 0.059 95.617); - --color-amber-200: oklch(0.924 0.12 95.746); - --color-amber-300: oklch(0.879 0.169 91.605); - --color-amber-400: oklch(0.828 0.189 84.429); - --color-amber-500: oklch(0.769 0.188 70.08); - --color-amber-600: oklch(0.666 0.179 58.318); - --color-amber-700: oklch(0.555 0.163 48.998); - --color-amber-800: oklch(0.473 0.137 46.201); - --color-amber-900: oklch(0.414 0.112 45.904); - --color-amber-950: oklch(0.279 0.077 45.635); + --color-amber-50: oklch(98.7% 0.022 95.277); + --color-amber-100: oklch(96.2% 0.059 95.617); + --color-amber-200: oklch(92.4% 0.12 95.746); + --color-amber-300: oklch(87.9% 0.169 91.605); + --color-amber-400: oklch(82.8% 0.189 84.429); + --color-amber-500: oklch(76.9% 0.188 70.08); + --color-amber-600: oklch(66.6% 0.179 58.318); + --color-amber-700: oklch(55.5% 0.163 48.998); + --color-amber-800: oklch(47.3% 0.137 46.201); + --color-amber-900: oklch(41.4% 0.112 45.904); + --color-amber-950: oklch(27.9% 0.077 45.635); - --color-yellow-50: oklch(0.987 0.026 102.212); - --color-yellow-100: oklch(0.973 0.071 103.193); - --color-yellow-200: oklch(0.945 0.129 101.54); - --color-yellow-300: oklch(0.905 0.182 98.111); - --color-yellow-400: oklch(0.852 0.199 91.936); - --color-yellow-500: oklch(0.795 0.184 86.047); - --color-yellow-600: oklch(0.681 0.162 75.834); - --color-yellow-700: oklch(0.554 0.135 66.442); - --color-yellow-800: oklch(0.476 0.114 61.907); - --color-yellow-900: oklch(0.421 0.095 57.708); - --color-yellow-950: oklch(0.286 0.066 53.813); + --color-yellow-50: oklch(98.7% 0.026 102.212); + --color-yellow-100: oklch(97.3% 0.071 103.193); + --color-yellow-200: oklch(94.5% 0.129 101.54); + --color-yellow-300: oklch(90.5% 0.182 98.111); + --color-yellow-400: oklch(85.2% 0.199 91.936); + --color-yellow-500: oklch(79.5% 0.184 86.047); + --color-yellow-600: oklch(68.1% 0.162 75.834); + --color-yellow-700: oklch(55.4% 0.135 66.442); + --color-yellow-800: oklch(47.6% 0.114 61.907); + --color-yellow-900: oklch(42.1% 0.095 57.708); + --color-yellow-950: oklch(28.6% 0.066 53.813); - --color-lime-50: oklch(0.986 0.031 120.757); - --color-lime-100: oklch(0.967 0.067 122.328); - --color-lime-200: oklch(0.938 0.127 124.321); - --color-lime-300: oklch(0.897 0.196 126.665); - --color-lime-400: oklch(0.841 0.238 128.85); - --color-lime-500: oklch(0.768 0.233 130.85); - --color-lime-600: oklch(0.648 0.2 131.684); - --color-lime-700: oklch(0.532 0.157 131.589); - --color-lime-800: oklch(0.453 0.124 130.933); - --color-lime-900: oklch(0.405 0.101 131.063); - --color-lime-950: oklch(0.274 0.072 132.109); + --color-lime-50: oklch(98.6% 0.031 120.757); + --color-lime-100: oklch(96.7% 0.067 122.328); + --color-lime-200: oklch(93.8% 0.127 124.321); + --color-lime-300: oklch(89.7% 0.196 126.665); + --color-lime-400: oklch(84.1% 0.238 128.85); + --color-lime-500: oklch(76.8% 0.233 130.85); + --color-lime-600: oklch(64.8% 0.2 131.684); + --color-lime-700: oklch(53.2% 0.157 131.589); + --color-lime-800: oklch(45.3% 0.124 130.933); + --color-lime-900: oklch(40.5% 0.101 131.063); + --color-lime-950: oklch(27.4% 0.072 132.109); - --color-green-50: oklch(0.982 0.018 155.826); - --color-green-100: oklch(0.962 0.044 156.743); - --color-green-200: oklch(0.925 0.084 155.995); - --color-green-300: oklch(0.871 0.15 154.449); - --color-green-400: oklch(0.792 0.209 151.711); - --color-green-500: oklch(0.723 0.219 149.579); - --color-green-600: oklch(0.627 0.194 149.214); - --color-green-700: oklch(0.527 0.154 150.069); - --color-green-800: oklch(0.448 0.119 151.328); - --color-green-900: oklch(0.393 0.095 152.535); - --color-green-950: oklch(0.266 0.065 152.934); + --color-green-50: oklch(98.2% 0.018 155.826); + --color-green-100: oklch(96.2% 0.044 156.743); + --color-green-200: oklch(92.5% 0.084 155.995); + --color-green-300: oklch(87.1% 0.15 154.449); + --color-green-400: oklch(79.2% 0.209 151.711); + --color-green-500: oklch(72.3% 0.219 149.579); + --color-green-600: oklch(62.7% 0.194 149.214); + --color-green-700: oklch(52.7% 0.154 150.069); + --color-green-800: oklch(44.8% 0.119 151.328); + --color-green-900: oklch(39.3% 0.095 152.535); + --color-green-950: oklch(26.6% 0.065 152.934); - --color-emerald-50: oklch(0.979 0.021 166.113); - --color-emerald-100: oklch(0.95 0.052 163.051); - --color-emerald-200: oklch(0.905 0.093 164.15); - --color-emerald-300: oklch(0.845 0.143 164.978); - --color-emerald-400: oklch(0.765 0.177 163.223); - --color-emerald-500: oklch(0.696 0.17 162.48); - --color-emerald-600: oklch(0.596 0.145 163.225); - --color-emerald-700: oklch(0.508 0.118 165.612); - --color-emerald-800: oklch(0.432 0.095 166.913); - --color-emerald-900: oklch(0.378 0.077 168.94); - --color-emerald-950: oklch(0.262 0.051 172.552); + --color-emerald-50: oklch(97.9% 0.021 166.113); + --color-emerald-100: oklch(95% 0.052 163.051); + --color-emerald-200: oklch(90.5% 0.093 164.15); + --color-emerald-300: oklch(84.5% 0.143 164.978); + --color-emerald-400: oklch(76.5% 0.177 163.223); + --color-emerald-500: oklch(69.6% 0.17 162.48); + --color-emerald-600: oklch(59.6% 0.145 163.225); + --color-emerald-700: oklch(50.8% 0.118 165.612); + --color-emerald-800: oklch(43.2% 0.095 166.913); + --color-emerald-900: oklch(37.8% 0.077 168.94); + --color-emerald-950: oklch(26.2% 0.051 172.552); - --color-teal-50: oklch(0.984 0.014 180.72); - --color-teal-100: oklch(0.953 0.051 180.801); - --color-teal-200: oklch(0.91 0.096 180.426); - --color-teal-300: oklch(0.855 0.138 181.071); - --color-teal-400: oklch(0.777 0.152 181.912); - --color-teal-500: oklch(0.704 0.14 182.503); - --color-teal-600: oklch(0.6 0.118 184.704); - --color-teal-700: oklch(0.511 0.096 186.391); - --color-teal-800: oklch(0.437 0.078 188.216); - --color-teal-900: oklch(0.386 0.063 188.416); - --color-teal-950: oklch(0.277 0.046 192.524); + --color-teal-50: oklch(98.4% 0.014 180.72); + --color-teal-100: oklch(95.3% 0.051 180.801); + --color-teal-200: oklch(91% 0.096 180.426); + --color-teal-300: oklch(85.5% 0.138 181.071); + --color-teal-400: oklch(77.7% 0.152 181.912); + --color-teal-500: oklch(70.4% 0.14 182.503); + --color-teal-600: oklch(60% 0.118 184.704); + --color-teal-700: oklch(51.1% 0.096 186.391); + --color-teal-800: oklch(43.7% 0.078 188.216); + --color-teal-900: oklch(38.6% 0.063 188.416); + --color-teal-950: oklch(27.7% 0.046 192.524); - --color-cyan-50: oklch(0.984 0.019 200.873); - --color-cyan-100: oklch(0.956 0.045 203.388); - --color-cyan-200: oklch(0.917 0.08 205.041); - --color-cyan-300: oklch(0.865 0.127 207.078); - --color-cyan-400: oklch(0.789 0.154 211.53); - --color-cyan-500: oklch(0.715 0.143 215.221); - --color-cyan-600: oklch(0.609 0.126 221.723); - --color-cyan-700: oklch(0.52 0.105 223.128); - --color-cyan-800: oklch(0.45 0.085 224.283); - --color-cyan-900: oklch(0.398 0.07 227.392); - --color-cyan-950: oklch(0.302 0.056 229.695); + --color-cyan-50: oklch(98.4% 0.019 200.873); + --color-cyan-100: oklch(95.6% 0.045 203.388); + --color-cyan-200: oklch(91.7% 0.08 205.041); + --color-cyan-300: oklch(86.5% 0.127 207.078); + --color-cyan-400: oklch(78.9% 0.154 211.53); + --color-cyan-500: oklch(71.5% 0.143 215.221); + --color-cyan-600: oklch(60.9% 0.126 221.723); + --color-cyan-700: oklch(52% 0.105 223.128); + --color-cyan-800: oklch(45% 0.085 224.283); + --color-cyan-900: oklch(39.8% 0.07 227.392); + --color-cyan-950: oklch(30.2% 0.056 229.695); - --color-sky-50: oklch(0.977 0.013 236.62); - --color-sky-100: oklch(0.951 0.026 236.824); - --color-sky-200: oklch(0.901 0.058 230.902); - --color-sky-300: oklch(0.828 0.111 230.318); - --color-sky-400: oklch(0.746 0.16 232.661); - --color-sky-500: oklch(0.685 0.169 237.323); - --color-sky-600: oklch(0.588 0.158 241.966); - --color-sky-700: oklch(0.5 0.134 242.749); - --color-sky-800: oklch(0.443 0.11 240.79); - --color-sky-900: oklch(0.391 0.09 240.876); - --color-sky-950: oklch(0.293 0.066 243.157); + --color-sky-50: oklch(97.7% 0.013 236.62); + --color-sky-100: oklch(95.1% 0.026 236.824); + --color-sky-200: oklch(90.1% 0.058 230.902); + --color-sky-300: oklch(82.8% 0.111 230.318); + --color-sky-400: oklch(74.6% 0.16 232.661); + --color-sky-500: oklch(68.5% 0.169 237.323); + --color-sky-600: oklch(58.8% 0.158 241.966); + --color-sky-700: oklch(50% 0.134 242.749); + --color-sky-800: oklch(44.3% 0.11 240.79); + --color-sky-900: oklch(39.1% 0.09 240.876); + --color-sky-950: oklch(29.3% 0.066 243.157); - --color-blue-50: oklch(0.97 0.014 254.604); - --color-blue-100: oklch(0.932 0.032 255.585); - --color-blue-200: oklch(0.882 0.059 254.128); - --color-blue-300: oklch(0.809 0.105 251.813); - --color-blue-400: oklch(0.707 0.165 254.624); - --color-blue-500: oklch(0.623 0.214 259.815); - --color-blue-600: oklch(0.546 0.245 262.881); - --color-blue-700: oklch(0.488 0.243 264.376); - --color-blue-800: oklch(0.424 0.199 265.638); - --color-blue-900: oklch(0.379 0.146 265.522); - --color-blue-950: oklch(0.282 0.091 267.935); + --color-blue-50: oklch(97% 0.014 254.604); + --color-blue-100: oklch(93.2% 0.032 255.585); + --color-blue-200: oklch(88.2% 0.059 254.128); + --color-blue-300: oklch(80.9% 0.105 251.813); + --color-blue-400: oklch(70.7% 0.165 254.624); + --color-blue-500: oklch(62.3% 0.214 259.815); + --color-blue-600: oklch(54.6% 0.245 262.881); + --color-blue-700: oklch(48.8% 0.243 264.376); + --color-blue-800: oklch(42.4% 0.199 265.638); + --color-blue-900: oklch(37.9% 0.146 265.522); + --color-blue-950: oklch(28.2% 0.091 267.935); - --color-indigo-50: oklch(0.962 0.018 272.314); - --color-indigo-100: oklch(0.93 0.034 272.788); - --color-indigo-200: oklch(0.87 0.065 274.039); - --color-indigo-300: oklch(0.785 0.115 274.713); - --color-indigo-400: oklch(0.673 0.182 276.935); - --color-indigo-500: oklch(0.585 0.233 277.117); - --color-indigo-600: oklch(0.511 0.262 276.966); - --color-indigo-700: oklch(0.457 0.24 277.023); - --color-indigo-800: oklch(0.398 0.195 277.366); - --color-indigo-900: oklch(0.359 0.144 278.697); - --color-indigo-950: oklch(0.257 0.09 281.288); + --color-indigo-50: oklch(96.2% 0.018 272.314); + --color-indigo-100: oklch(93% 0.034 272.788); + --color-indigo-200: oklch(87% 0.065 274.039); + --color-indigo-300: oklch(78.5% 0.115 274.713); + --color-indigo-400: oklch(67.3% 0.182 276.935); + --color-indigo-500: oklch(58.5% 0.233 277.117); + --color-indigo-600: oklch(51.1% 0.262 276.966); + --color-indigo-700: oklch(45.7% 0.24 277.023); + --color-indigo-800: oklch(39.8% 0.195 277.366); + --color-indigo-900: oklch(35.9% 0.144 278.697); + --color-indigo-950: oklch(25.7% 0.09 281.288); - --color-violet-50: oklch(0.969 0.016 293.756); - --color-violet-100: oklch(0.943 0.029 294.588); - --color-violet-200: oklch(0.894 0.057 293.283); - --color-violet-300: oklch(0.811 0.111 293.571); - --color-violet-400: oklch(0.702 0.183 293.541); - --color-violet-500: oklch(0.606 0.25 292.717); - --color-violet-600: oklch(0.541 0.281 293.009); - --color-violet-700: oklch(0.491 0.27 292.581); - --color-violet-800: oklch(0.432 0.232 292.759); - --color-violet-900: oklch(0.38 0.189 293.745); - --color-violet-950: oklch(0.283 0.141 291.089); + --color-violet-50: oklch(96.9% 0.016 293.756); + --color-violet-100: oklch(94.3% 0.029 294.588); + --color-violet-200: oklch(89.4% 0.057 293.283); + --color-violet-300: oklch(81.1% 0.111 293.571); + --color-violet-400: oklch(70.2% 0.183 293.541); + --color-violet-500: oklch(60.6% 0.25 292.717); + --color-violet-600: oklch(54.1% 0.281 293.009); + --color-violet-700: oklch(49.1% 0.27 292.581); + --color-violet-800: oklch(43.2% 0.232 292.759); + --color-violet-900: oklch(38% 0.189 293.745); + --color-violet-950: oklch(28.3% 0.141 291.089); - --color-purple-50: oklch(0.977 0.014 308.299); - --color-purple-100: oklch(0.946 0.033 307.174); - --color-purple-200: oklch(0.902 0.063 306.703); - --color-purple-300: oklch(0.827 0.119 306.383); - --color-purple-400: oklch(0.714 0.203 305.504); - --color-purple-500: oklch(0.627 0.265 303.9); - --color-purple-600: oklch(0.558 0.288 302.321); - --color-purple-700: oklch(0.496 0.265 301.924); - --color-purple-800: oklch(0.438 0.218 303.724); - --color-purple-900: oklch(0.381 0.176 304.987); - --color-purple-950: oklch(0.291 0.149 302.717); + --color-purple-50: oklch(97.7% 0.014 308.299); + --color-purple-100: oklch(94.6% 0.033 307.174); + --color-purple-200: oklch(90.2% 0.063 306.703); + --color-purple-300: oklch(82.7% 0.119 306.383); + --color-purple-400: oklch(71.4% 0.203 305.504); + --color-purple-500: oklch(62.7% 0.265 303.9); + --color-purple-600: oklch(55.8% 0.288 302.321); + --color-purple-700: oklch(49.6% 0.265 301.924); + --color-purple-800: oklch(43.8% 0.218 303.724); + --color-purple-900: oklch(38.1% 0.176 304.987); + --color-purple-950: oklch(29.1% 0.149 302.717); - --color-fuchsia-50: oklch(0.977 0.017 320.058); - --color-fuchsia-100: oklch(0.952 0.037 318.852); - --color-fuchsia-200: oklch(0.903 0.076 319.62); - --color-fuchsia-300: oklch(0.833 0.145 321.434); - --color-fuchsia-400: oklch(0.74 0.238 322.16); - --color-fuchsia-500: oklch(0.667 0.295 322.15); - --color-fuchsia-600: oklch(0.591 0.293 322.896); - --color-fuchsia-700: oklch(0.518 0.253 323.949); - --color-fuchsia-800: oklch(0.452 0.211 324.591); - --color-fuchsia-900: oklch(0.401 0.17 325.612); - --color-fuchsia-950: oklch(0.293 0.136 325.661); + --color-fuchsia-50: oklch(97.7% 0.017 320.058); + --color-fuchsia-100: oklch(95.2% 0.037 318.852); + --color-fuchsia-200: oklch(90.3% 0.076 319.62); + --color-fuchsia-300: oklch(83.3% 0.145 321.434); + --color-fuchsia-400: oklch(74% 0.238 322.16); + --color-fuchsia-500: oklch(66.7% 0.295 322.15); + --color-fuchsia-600: oklch(59.1% 0.293 322.896); + --color-fuchsia-700: oklch(51.8% 0.253 323.949); + --color-fuchsia-800: oklch(45.2% 0.211 324.591); + --color-fuchsia-900: oklch(40.1% 0.17 325.612); + --color-fuchsia-950: oklch(29.3% 0.136 325.661); - --color-pink-50: oklch(0.971 0.014 343.198); - --color-pink-100: oklch(0.948 0.028 342.258); - --color-pink-200: oklch(0.899 0.061 343.231); - --color-pink-300: oklch(0.823 0.12 346.018); - --color-pink-400: oklch(0.718 0.202 349.761); - --color-pink-500: oklch(0.656 0.241 354.308); - --color-pink-600: oklch(0.592 0.249 0.584); - --color-pink-700: oklch(0.525 0.223 3.958); - --color-pink-800: oklch(0.459 0.187 3.815); - --color-pink-900: oklch(0.408 0.153 2.432); - --color-pink-950: oklch(0.284 0.109 3.907); + --color-pink-50: oklch(97.1% 0.014 343.198); + --color-pink-100: oklch(94.8% 0.028 342.258); + --color-pink-200: oklch(89.9% 0.061 343.231); + --color-pink-300: oklch(82.3% 0.12 346.018); + --color-pink-400: oklch(71.8% 0.202 349.761); + --color-pink-500: oklch(65.6% 0.241 354.308); + --color-pink-600: oklch(59.2% 0.249 0.584); + --color-pink-700: oklch(52.5% 0.223 3.958); + --color-pink-800: oklch(45.9% 0.187 3.815); + --color-pink-900: oklch(40.8% 0.153 2.432); + --color-pink-950: oklch(28.4% 0.109 3.907); - --color-rose-50: oklch(0.969 0.015 12.422); - --color-rose-100: oklch(0.941 0.03 12.58); - --color-rose-200: oklch(0.892 0.058 10.001); - --color-rose-300: oklch(0.81 0.117 11.638); - --color-rose-400: oklch(0.712 0.194 13.428); - --color-rose-500: oklch(0.645 0.246 16.439); - --color-rose-600: oklch(0.586 0.253 17.585); - --color-rose-700: oklch(0.514 0.222 16.935); - --color-rose-800: oklch(0.455 0.188 13.697); - --color-rose-900: oklch(0.41 0.159 10.272); - --color-rose-950: oklch(0.271 0.105 12.094); + --color-rose-50: oklch(96.9% 0.015 12.422); + --color-rose-100: oklch(94.1% 0.03 12.58); + --color-rose-200: oklch(89.2% 0.058 10.001); + --color-rose-300: oklch(81% 0.117 11.638); + --color-rose-400: oklch(71.2% 0.194 13.428); + --color-rose-500: oklch(64.5% 0.246 16.439); + --color-rose-600: oklch(58.6% 0.253 17.585); + --color-rose-700: oklch(51.4% 0.222 16.935); + --color-rose-800: oklch(45.5% 0.188 13.697); + --color-rose-900: oklch(41% 0.159 10.272); + --color-rose-950: oklch(27.1% 0.105 12.094); - --color-slate-50: oklch(0.984 0.003 247.858); - --color-slate-100: oklch(0.968 0.007 247.896); - --color-slate-200: oklch(0.929 0.013 255.508); - --color-slate-300: oklch(0.869 0.022 252.894); - --color-slate-400: oklch(0.704 0.04 256.788); - --color-slate-500: oklch(0.554 0.046 257.417); - --color-slate-600: oklch(0.446 0.043 257.281); - --color-slate-700: oklch(0.372 0.044 257.287); - --color-slate-800: oklch(0.279 0.041 260.031); - --color-slate-900: oklch(0.208 0.042 265.755); - --color-slate-950: oklch(0.129 0.042 264.695); + --color-slate-50: oklch(98.4% 0.003 247.858); + --color-slate-100: oklch(96.8% 0.007 247.896); + --color-slate-200: oklch(92.9% 0.013 255.508); + --color-slate-300: oklch(86.9% 0.022 252.894); + --color-slate-400: oklch(70.4% 0.04 256.788); + --color-slate-500: oklch(55.4% 0.046 257.417); + --color-slate-600: oklch(44.6% 0.043 257.281); + --color-slate-700: oklch(37.2% 0.044 257.287); + --color-slate-800: oklch(27.9% 0.041 260.031); + --color-slate-900: oklch(20.8% 0.042 265.755); + --color-slate-950: oklch(12.9% 0.042 264.695); - --color-gray-50: oklch(0.985 0.002 247.839); - --color-gray-100: oklch(0.967 0.003 264.542); - --color-gray-200: oklch(0.928 0.006 264.531); - --color-gray-300: oklch(0.872 0.01 258.338); - --color-gray-400: oklch(0.707 0.022 261.325); - --color-gray-500: oklch(0.551 0.027 264.364); - --color-gray-600: oklch(0.446 0.03 256.802); - --color-gray-700: oklch(0.373 0.034 259.733); - --color-gray-800: oklch(0.278 0.033 256.848); - --color-gray-900: oklch(0.21 0.034 264.665); - --color-gray-950: oklch(0.13 0.028 261.692); + --color-gray-50: oklch(98.5% 0.002 247.839); + --color-gray-100: oklch(96.7% 0.003 264.542); + --color-gray-200: oklch(92.8% 0.006 264.531); + --color-gray-300: oklch(87.2% 0.01 258.338); + --color-gray-400: oklch(70.7% 0.022 261.325); + --color-gray-500: oklch(55.1% 0.027 264.364); + --color-gray-600: oklch(44.6% 0.03 256.802); + --color-gray-700: oklch(37.3% 0.034 259.733); + --color-gray-800: oklch(27.8% 0.033 256.848); + --color-gray-900: oklch(21% 0.034 264.665); + --color-gray-950: oklch(13% 0.028 261.692); - --color-zinc-50: oklch(0.985 0 0); - --color-zinc-100: oklch(0.967 0.001 286.375); - --color-zinc-200: oklch(0.92 0.004 286.32); - --color-zinc-300: oklch(0.871 0.006 286.286); - --color-zinc-400: oklch(0.705 0.015 286.067); - --color-zinc-500: oklch(0.552 0.016 285.938); - --color-zinc-600: oklch(0.442 0.017 285.786); - --color-zinc-700: oklch(0.37 0.013 285.805); - --color-zinc-800: oklch(0.274 0.006 286.033); - --color-zinc-900: oklch(0.21 0.006 285.885); - --color-zinc-950: oklch(0.141 0.005 285.823); + --color-zinc-50: oklch(98.5% 0 none); + --color-zinc-100: oklch(96.7% 0.001 286.375); + --color-zinc-200: oklch(92% 0.004 286.32); + --color-zinc-300: oklch(87.1% 0.006 286.286); + --color-zinc-400: oklch(70.5% 0.015 286.067); + --color-zinc-500: oklch(55.2% 0.016 285.938); + --color-zinc-600: oklch(44.2% 0.017 285.786); + --color-zinc-700: oklch(37% 0.013 285.805); + --color-zinc-800: oklch(27.4% 0.006 286.033); + --color-zinc-900: oklch(21% 0.006 285.885); + --color-zinc-950: oklch(14.1% 0.005 285.823); - --color-neutral-50: oklch(0.985 0 0); - --color-neutral-100: oklch(0.97 0 0); - --color-neutral-200: oklch(0.922 0 0); - --color-neutral-300: oklch(0.87 0 0); - --color-neutral-400: oklch(0.708 0 0); - --color-neutral-500: oklch(0.556 0 0); - --color-neutral-600: oklch(0.439 0 0); - --color-neutral-700: oklch(0.371 0 0); - --color-neutral-800: oklch(0.269 0 0); - --color-neutral-900: oklch(0.205 0 0); - --color-neutral-950: oklch(0.145 0 0); + --color-neutral-50: oklch(98.5% 0 none); + --color-neutral-100: oklch(97% 0 none); + --color-neutral-200: oklch(92.2% 0 none); + --color-neutral-300: oklch(87% 0 none); + --color-neutral-400: oklch(70.8% 0 none); + --color-neutral-500: oklch(55.6% 0 none); + --color-neutral-600: oklch(43.9% 0 none); + --color-neutral-700: oklch(37.1% 0 none); + --color-neutral-800: oklch(26.9% 0 none); + --color-neutral-900: oklch(20.5% 0 none); + --color-neutral-950: oklch(14.5% 0 none); - --color-stone-50: oklch(0.985 0.001 106.423); - --color-stone-100: oklch(0.97 0.001 106.424); - --color-stone-200: oklch(0.923 0.003 48.717); - --color-stone-300: oklch(0.869 0.005 56.366); - --color-stone-400: oklch(0.709 0.01 56.259); - --color-stone-500: oklch(0.553 0.013 58.071); - --color-stone-600: oklch(0.444 0.011 73.639); - --color-stone-700: oklch(0.374 0.01 67.558); - --color-stone-800: oklch(0.268 0.007 34.298); - --color-stone-900: oklch(0.216 0.006 56.043); - --color-stone-950: oklch(0.147 0.004 49.25); + --color-stone-50: oklch(98.5% 0.001 106.423); + --color-stone-100: oklch(97% 0.001 106.424); + --color-stone-200: oklch(92.3% 0.003 48.717); + --color-stone-300: oklch(86.9% 0.005 56.366); + --color-stone-400: oklch(70.9% 0.01 56.259); + --color-stone-500: oklch(55.3% 0.013 58.071); + --color-stone-600: oklch(44.4% 0.011 73.639); + --color-stone-700: oklch(37.4% 0.01 67.558); + --color-stone-800: oklch(26.8% 0.007 34.298); + --color-stone-900: oklch(21.6% 0.006 56.043); + --color-stone-950: oklch(14.7% 0.004 49.25); + + --color-mauve-50: oklch(98.5% 0 none); + --color-mauve-100: oklch(96% 0.003 325.6); + --color-mauve-200: oklch(92.2% 0.005 325.62); + --color-mauve-300: oklch(86.5% 0.012 325.68); + --color-mauve-400: oklch(71.1% 0.019 323.02); + --color-mauve-500: oklch(54.2% 0.034 322.5); + --color-mauve-600: oklch(43.5% 0.029 321.78); + --color-mauve-700: oklch(36.4% 0.029 323.89); + --color-mauve-800: oklch(26.3% 0.024 320.12); + --color-mauve-900: oklch(21.2% 0.019 322.12); + --color-mauve-950: oklch(14.5% 0.008 326); + + --color-olive-50: oklch(98.8% 0.003 106.5); + --color-olive-100: oklch(96.6% 0.005 106.5); + --color-olive-200: oklch(93% 0.007 106.5); + --color-olive-300: oklch(88% 0.011 106.6); + --color-olive-400: oklch(73.7% 0.021 106.9); + --color-olive-500: oklch(58% 0.031 107.3); + --color-olive-600: oklch(46.6% 0.025 107.3); + --color-olive-700: oklch(39.4% 0.023 107.4); + --color-olive-800: oklch(28.6% 0.016 107.4); + --color-olive-900: oklch(22.8% 0.013 107.4); + --color-olive-950: oklch(15.3% 0.006 107.1); + + --color-mist-50: oklch(98.7% 0.002 197.1); + --color-mist-100: oklch(96.3% 0.002 197.1); + --color-mist-200: oklch(92.5% 0.005 214.3); + --color-mist-300: oklch(87.2% 0.007 219.6); + --color-mist-400: oklch(72.3% 0.014 214.4); + --color-mist-500: oklch(56% 0.021 213.5); + --color-mist-600: oklch(45% 0.017 213.2); + --color-mist-700: oklch(37.8% 0.015 216); + --color-mist-800: oklch(27.5% 0.011 216.9); + --color-mist-900: oklch(21.8% 0.008 223.9); + --color-mist-950: oklch(14.8% 0.004 228.8); + + --color-taupe-50: oklch(98.6% 0.002 67.8); + --color-taupe-100: oklch(96% 0.002 17.2); + --color-taupe-200: oklch(92.2% 0.005 34.3); + --color-taupe-300: oklch(86.8% 0.007 39.5); + --color-taupe-400: oklch(71.4% 0.014 41.2); + --color-taupe-500: oklch(54.7% 0.021 43.1); + --color-taupe-600: oklch(43.8% 0.017 39.3); + --color-taupe-700: oklch(36.7% 0.016 35.7); + --color-taupe-800: oklch(26.8% 0.011 36.5); + --color-taupe-900: oklch(21.4% 0.009 43.1); + --color-taupe-950: oklch(14.7% 0.004 49.3); --color-black: #000; --color-white: #fff; @@ -293,7 +343,6 @@ --container-5xl: 64rem; --container-6xl: 72rem; --container-7xl: 80rem; - --container-prose: 65ch; --text-xs: 0.75rem; --text-xs--line-height: calc(1 / 0.75); @@ -373,6 +422,15 @@ --drop-shadow-xl: 0 9px 7px rgb(0 0 0 / 0.1); --drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15); + --text-shadow-2xs: 0px 1px 0px rgb(0 0 0 / 0.15); + --text-shadow-xs: 0px 1px 1px rgb(0 0 0 / 0.2); + --text-shadow-sm: + 0px 1px 0px rgb(0 0 0 / 0.075), 0px 1px 1px rgb(0 0 0 / 0.075), 0px 2px 2px rgb(0 0 0 / 0.075); + --text-shadow-md: + 0px 1px 1px rgb(0 0 0 / 0.1), 0px 1px 2px rgb(0 0 0 / 0.1), 0px 2px 4px rgb(0 0 0 / 0.1); + --text-shadow-lg: + 0px 1px 2px rgb(0 0 0 / 0.1), 0px 3px 2px rgb(0 0 0 / 0.1), 0px 4px 8px rgb(0 0 0 / 0.1); + --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); @@ -433,12 +491,12 @@ --default-transition-duration: 150ms; --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - --default-font-family: var(--font-sans); - --default-font-feature-settings: var(--font-sans--font-feature-settings); - --default-font-variation-settings: var(--font-sans--font-variation-settings); - --default-mono-font-family: var(--font-mono); - --default-mono-font-feature-settings: var(--font-mono--font-feature-settings); - --default-mono-font-variation-settings: var(--font-mono--font-variation-settings); + --default-font-family: --theme(--font-sans, initial); + --default-font-feature-settings: --theme(--font-sans--font-feature-settings, initial); + --default-font-variation-settings: --theme(--font-sans--font-variation-settings, initial); + --default-mono-font-family: --theme(--font-mono, initial); + --default-mono-font-feature-settings: --theme(--font-mono--font-feature-settings, initial); + --default-mono-font-variation-settings: --theme(--font-mono--font-variation-settings, initial); } /* Deprecated */ @@ -448,4 +506,5 @@ --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); --drop-shadow: 0 1px 2px rgb(0 0 0 / 0.1), 0 1px 1px rgb(0 0 0 / 0.06); --radius: 0.25rem; + --max-width-prose: 65ch; } diff --git a/packages/tailwindcss/tsconfig.json b/packages/tailwindcss/tsconfig.json index 6ae022f65..d2131cbeb 100644 --- a/packages/tailwindcss/tsconfig.json +++ b/packages/tailwindcss/tsconfig.json @@ -1,3 +1,6 @@ { "extends": "../tsconfig.base.json", + "compilerOptions": { + "verbatimModuleSyntax": false, + }, } diff --git a/packages/tailwindcss/tsup.config.ts b/packages/tailwindcss/tsup.config.ts index 59c8bdfa2..6bc831056 100644 --- a/packages/tailwindcss/tsup.config.ts +++ b/packages/tailwindcss/tsup.config.ts @@ -12,6 +12,9 @@ export default defineConfig([ 'default-theme': 'src/compat/default-theme.ts', 'flatten-color-palette': 'src/compat/flatten-color-palette.ts', }, + define: { + 'process.env.FEATURES_ENV': JSON.stringify(process.env.FEATURES_ENV ?? 'insiders'), + }, }, { format: ['cjs'], @@ -24,5 +27,8 @@ export default defineConfig([ 'default-theme': 'src/compat/default-theme.cts', 'flatten-color-palette': 'src/compat/flatten-color-palette.cts', }, + define: { + 'process.env.FEATURES_ENV': JSON.stringify(process.env.FEATURES_ENV ?? 'insiders'), + }, }, ]) diff --git a/packages/tailwindcss/vitest.config.ts b/packages/tailwindcss/vitest.config.mts similarity index 100% rename from packages/tailwindcss/vitest.config.ts rename to packages/tailwindcss/vitest.config.mts diff --git a/patches/@napi-rs__cli@3.7.4.patch b/patches/@napi-rs__cli@3.7.4.patch new file mode 100644 index 000000000..e72212499 --- /dev/null +++ b/patches/@napi-rs__cli@3.7.4.patch @@ -0,0 +1,80 @@ +diff --git a/dist/cli.js b/dist/cli.js +index c4c2d568bc070c07dbf3a1f9f4a0c946466668cf..353bbca86e3261a2ba9a8fc56dc612c7c84dab6d 100755 +--- a/dist/cli.js ++++ b/dist/cli.js +@@ -1,4 +1,13 @@ + #!/usr/bin/env node ++// PATCHED (see patches/@napi-rs__cli@3.7.4.patch): the embedded wasi loader ++// and worker templates below retry `new WASI(...)` with narrower preopens, ++// because preopening `/` throws `UVWASI_EACCES` on sandboxed platforms (e.g. ++// OpenHarmony), which would make the generated wasm binding fail to load. ++// ++// The same templates also exist in `dist/index.js` and `dist/index.cjs` (the ++// programmatic API). Those are intentionally NOT patched because we only ++// build through the `napi` bin, which runs this file. If we ever start using ++// the programmatic API, update the patch to cover those bundles too. + import { createRequire } from "node:module"; + import { Cli, Command, Option } from "clipanion"; + import path, { basename, dirname, isAbsolute, join, parse, resolve } from "node:path"; +@@ -1021,13 +1030,25 @@ const { + + const __rootDir = __nodePath.parse(process.cwd()).root + +-const __wasi = new __nodeWASI({ +- version: 'preview1', +- env: process.env, +- preopens: { +- [__rootDir]: __rootDir, ++const __wasi = (() => { ++ // Preopening '/' fails with UVWASI_EACCES in sandboxed environments (e.g. ++ // OpenHarmony, Android), which would prevent the wasm binding from loading ++ // at all. Retry with narrower preopens instead. Without any preopens the ++ // binding still loads; only file system access is unavailable. ++ let lastError = null ++ for (const dir of [__rootDir, process.cwd(), null]) { ++ try { ++ return new __nodeWASI({ ++ version: 'preview1', ++ env: process.env, ++ preopens: dir === null ? {} : { [dir]: dir }, ++ }) ++ } catch (error) { ++ lastError = error ++ } + } +-}) ++ throw lastError ++})() + + const __emnapiContext = __emnapiGetDefaultContext() + +@@ -1151,13 +1172,22 @@ const __rootDir = parse(process.cwd()).root; + + const handler = new MessageHandler({ + onLoad({ wasmModule, wasmMemory }) { +- const wasi = new WASI({ +- version: 'preview1', +- env: process.env, +- preopens: { +- [__rootDir]: __rootDir, +- }, +- }); ++ // Keep in sync with the preopen fallback in the main-thread loader. ++ const wasi = (() => { ++ let lastError = null; ++ for (const dir of [__rootDir, process.cwd(), null]) { ++ try { ++ return new WASI({ ++ version: 'preview1', ++ env: process.env, ++ preopens: dir === null ? {} : { [dir]: dir }, ++ }); ++ } catch (error) { ++ lastError = error; ++ } ++ } ++ throw lastError; ++ })(); + + return instantiateNapiModuleSync(wasmModule, { + childThread: true, diff --git a/patches/@parcel__watcher@2.5.0.patch b/patches/@parcel__watcher@2.5.0.patch deleted file mode 100644 index 4f0f223ff..000000000 --- a/patches/@parcel__watcher@2.5.0.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/index.js b/index.js -index 8afb2b1126dcc687b7ff9b631589da252c1f9c22..f3b1158eb5612235388ff5f5347f6edcbf323090 100644 ---- a/index.js -+++ b/index.js -@@ -1,41 +1,27 @@ --const {createWrapper} = require('./wrapper'); -+const { createWrapper } = require('./wrapper') - --let name = `@parcel/watcher-${process.platform}-${process.arch}`; --if (process.platform === 'linux') { -- const { MUSL, family } = require('detect-libc'); -- if (family === MUSL) { -- name += '-musl'; -- } else { -- name += '-glibc'; -- } --} -+function loadPackage() { -+ if (process.platform === 'linux') { -+ let { MUSL, GLIBC, family, familySync } = require('detect-libc') -+ // Bun polyfills `detect-libc` in compiled binaries. We rely on -+ // detect-libc@1.0.3 but the polyfilled version is 2.x. In detect-libc@2x -+ // there is a `familySync` function that we can use instead. -+ if (typeof familySync === 'function') family = familySync() - --let binding; --try { -- binding = require(name); --} catch (err) { -- handleError(err); -- try { -- binding = require('./build/Release/watcher.node'); -- } catch (err) { -- handleError(err); -- try { -- binding = require('./build/Debug/watcher.node'); -- } catch (err) { -- handleError(err); -- throw new Error(`No prebuild or local build of @parcel/watcher found. Tried ${name}. Please ensure it is installed (don't use --no-optional when installing with npm). Otherwise it is possible we don't support your platform yet. If this is the case, please report an issue to https://github.com/parcel-bundler/watcher.`); -+ if (family === MUSL) { -+ return require(`@parcel/watcher-${process.platform}-${process.arch}-musl`) -+ } else if (family === GLIBC) { -+ return require(`@parcel/watcher-${process.platform}-${process.arch}-glibc`) -+ } else { -+ throw new Error(`Unsupported libc on: ${process.platform}-${process.arch}`) - } -+ } else { -+ return require(`@parcel/watcher-${process.platform}-${process.arch}`) - } - } - --function handleError(err) { -- if (err?.code !== 'MODULE_NOT_FOUND') { -- throw err; -- } --} -- --const wrapper = createWrapper(binding); --exports.writeSnapshot = wrapper.writeSnapshot; --exports.getEventsSince = wrapper.getEventsSince; --exports.subscribe = wrapper.subscribe; --exports.unsubscribe = wrapper.unsubscribe; -+const wrapper = createWrapper(loadPackage()) -+exports.writeSnapshot = wrapper.writeSnapshot -+exports.getEventsSince = wrapper.getEventsSince -+exports.subscribe = wrapper.subscribe -+exports.unsubscribe = wrapper.unsubscribe diff --git a/patches/@parcel__watcher@2.6.0.patch b/patches/@parcel__watcher@2.6.0.patch new file mode 100644 index 000000000..37ff83f04 --- /dev/null +++ b/patches/@parcel__watcher@2.6.0.patch @@ -0,0 +1,70 @@ +diff --git a/index.js b/index.js +index 78ee744d74900c39a839cafa46b295258fc86307..f28fd092644ccfb8aff2618b966cb5219a181a02 100644 +--- a/index.js ++++ b/index.js +@@ -1,43 +1,32 @@ + const {createWrapper} = require('./wrapper'); + +-let name = `@parcel/watcher-${process.platform}-${process.arch}`; +-if (process.platform === 'linux') { +- const {MUSL, familySync} = require('detect-libc'); +- const family = familySync(); +- if (family === MUSL) { +- name += '-musl'; +- } else { +- name += '-glibc'; +- } +-} ++// We need to take the `process.env.PLATFORM_LIBC` into account, and we also ++// need static analysis, so we can't make use of the `parts.push(…)` approach. ++function loadPackage() { ++ if (process.platform === 'linux') { ++ if (process.env.PLATFORM_LIBC === "musl") { ++ return require(`@parcel/watcher-${process.platform}-${process.arch}-musl`) ++ } else if (process.env.PLATFORM_LIBC === "glibc") { ++ return require(`@parcel/watcher-${process.platform}-${process.arch}-glibc`) ++ } else { ++ let { MUSL, GLIBC, familySync } = require('detect-libc') ++ let family = familySync() + +-let binding; +-try { +- binding = require(name); +-} catch (err) { +- handleError(err); +- try { +- binding = require('./build/Release/watcher.node'); +- } catch (err) { +- handleError(err); +- try { +- binding = require('./build/Debug/watcher.node'); +- } catch (err) { +- handleError(err); +- throw new Error( +- `No prebuild or local build of @parcel/watcher found. Tried ${name}. Please ensure it is installed (don't use --no-optional when installing with npm). Otherwise it is possible we don't support your platform yet. If this is the case, please report an issue to https://github.com/parcel-bundler/watcher.`, +- ); +- } +- } +-} + +-function handleError(err) { +- if (err?.code !== 'MODULE_NOT_FOUND') { +- throw err; ++ if (family === MUSL) { ++ return require(`@parcel/watcher-${process.platform}-${process.arch}-musl`) ++ } else if (family === GLIBC) { ++ return require(`@parcel/watcher-${process.platform}-${process.arch}-glibc`) ++ } else { ++ throw new Error(`Unsupported libc on: ${process.platform}-${process.arch}`) ++ } ++ } ++ } else { ++ return require(`@parcel/watcher-${process.platform}-${process.arch}`) + } + } + +-const wrapper = createWrapper(binding); ++const wrapper = createWrapper(loadPackage()); + exports.writeSnapshot = wrapper.writeSnapshot; + exports.getEventsSince = wrapper.getEventsSince; + exports.subscribe = wrapper.subscribe; diff --git a/patches/lightningcss@1.26.0.patch b/patches/lightningcss@1.26.0.patch deleted file mode 100644 index 1b83d90c8..000000000 --- a/patches/lightningcss@1.26.0.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/node/index.js b/node/index.js -index a9f2f6d5f3394329fcf8bc06af549030c01167a5..b92cc804adb694dbb17ee48368f13a03dd729267 100644 ---- a/node/index.js -+++ b/node/index.js -@@ -1,27 +1,32 @@ --let parts = [process.platform, process.arch]; --if (process.platform === 'linux') { -- const { MUSL, family } = require('detect-libc'); -- if (family === MUSL) { -- parts.push('musl'); -- } else if (process.arch === 'arm') { -- parts.push('gnueabihf'); -- } else { -- parts.push('gnu'); -- } --} else if (process.platform === 'win32') { -- parts.push('msvc'); --} -+function loadPackage() { -+ if (process.platform === "linux") { -+ let { MUSL, GLIBC, family, familySync } = require("detect-libc"); -+ // Bun polyfills `detect-libc` in compiled binaries. We rely on -+ // detect-libc@1.0.3 but the polyfilled version is 2.x. In detect-libc@2x -+ // there is a `familySync` function that we can use instead. -+ if (typeof familySync === 'function') family = familySync() - --if (process.env.CSS_TRANSFORMER_WASM) { -- module.exports = require(`../pkg`); --} else { -- try { -- module.exports = require(`lightningcss-${parts.join('-')}`); -- } catch (err) { -- module.exports = require(`../lightningcss.${parts.join('-')}.node`); -+ if (family === MUSL) { -+ return require(`lightningcss-${process.platform}-${process.arch}-musl`); -+ } else if (family === GLIBC) { -+ if (process.arch === "arm") { -+ return require(`lightningcss-${process.platform}-${process.arch}-gnueabihf`); -+ } else { -+ return require(`lightningcss-${process.platform}-${process.arch}-gnu`); -+ } -+ } else { -+ throw new Error( -+ `Unsupported libc on: ${process.platform}-${process.arch}` -+ ); -+ } -+ } else if (process.platform === "win32") { -+ return require(`lightningcss-${process.platform}-${process.arch}-msvc`); -+ } else { -+ return require(`lightningcss-${process.platform}-${process.arch}`); - } - } - --module.exports.browserslistToTargets = require('./browserslistToTargets'); --module.exports.composeVisitors = require('./composeVisitors'); --module.exports.Features = require('./flags').Features; -+module.exports = loadPackage(); -+module.exports.browserslistToTargets = require("./browserslistToTargets"); -+module.exports.composeVisitors = require("./composeVisitors"); -+module.exports.Features = require("./flags").Features; diff --git a/patches/lightningcss@1.33.0.patch b/patches/lightningcss@1.33.0.patch new file mode 100644 index 000000000..e70be2315 --- /dev/null +++ b/patches/lightningcss@1.33.0.patch @@ -0,0 +1,62 @@ +diff --git a/node/index.js b/node/index.js +index 6fe25aef457aa5448f9ee6204da1e253f5534800..742be0229b94450b631b6e069ffc372d2b35df47 100644 +--- a/node/index.js ++++ b/node/index.js +@@ -1,25 +1,40 @@ +-let parts = [process.platform, process.arch]; +-if (process.platform === 'linux') { +- const { MUSL, familySync } = require('detect-libc'); +- const family = familySync(); +- if (family === MUSL) { +- parts.push('musl'); +- } else if (process.arch === 'arm') { +- parts.push('gnueabihf'); ++// We need to take the `process.env.PLATFORM_LIBC` into account, and we also ++// need static analysis, so we can't make use of the `parts.push(…)` approach. ++function loadPackage() { ++ if (process.platform === 'linux') { ++ if (process.env.PLATFORM_LIBC === 'musl') { ++ return require(`lightningcss-${process.platform}-${process.arch}-musl`) ++ } else if (process.env.PLATFORM_LIBC === 'glibc') { ++ if (process.arch === 'arm') { ++ return require(`lightningcss-${process.platform}-${process.arch}-gnueabihf`) ++ } else { ++ return require(`lightningcss-${process.platform}-${process.arch}-gnu`) ++ } ++ } else { ++ let { MUSL, GLIBC, familySync } = require('detect-libc') ++ let family = familySync() ++ ++ if (family === MUSL) { ++ return require(`lightningcss-${process.platform}-${process.arch}-musl`) ++ } else if (family === GLIBC) { ++ if (process.arch === 'arm') { ++ return require(`lightningcss-${process.platform}-${process.arch}-gnueabihf`) ++ } else { ++ return require(`lightningcss-${process.platform}-${process.arch}-gnu`) ++ } ++ } else { ++ throw new Error(`Unsupported libc on: ${process.platform}-${process.arch}`) ++ } ++ } ++ } else if (process.platform === 'win32') { ++ return require(`lightningcss-${process.platform}-${process.arch}-msvc`) + } else { +- parts.push('gnu'); ++ return require(`lightningcss-${process.platform}-${process.arch}`) + } +-} else if (process.platform === 'win32') { +- parts.push('msvc'); + } + +-let native; +-try { +- native = require(`lightningcss-${parts.join('-')}`); +-} catch (err) { +- native = require(`../lightningcss.${parts.join('-')}.node`); +-} + ++let native = loadPackage(); + module.exports.transform = wrap(native.transform); + module.exports.transformStyleAttribute = wrap(native.transformStyleAttribute); + module.exports.bundle = wrap(native.bundle); diff --git a/playgrounds/nextjs/app/layout.tsx b/playgrounds/nextjs/app/layout.tsx index cf832e94e..41ee7f492 100644 --- a/playgrounds/nextjs/app/layout.tsx +++ b/playgrounds/nextjs/app/layout.tsx @@ -15,7 +15,7 @@ export default function RootLayout({ children: React.ReactNode }>) { return ( - + {/* */} {children} diff --git a/playgrounds/nextjs/app/page.module.css b/playgrounds/nextjs/app/page.module.css index e0c1bf51e..b0117f44c 100644 --- a/playgrounds/nextjs/app/page.module.css +++ b/playgrounds/nextjs/app/page.module.css @@ -1,228 +1,5 @@ -.main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 6rem; - min-height: 100vh; -} - -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); -} - -.description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; -} - -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); -} - -.code { - font-weight: 700; - font-family: var(--font-mono); -} - -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); -} - -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: - background 200ms, - border 200ms; -} - -.card span { - display: inline-block; - transition: transform 200ms; -} - -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; -} - -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; - text-wrap: balance; -} - -.center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient(to bottom, transparent 0%, rgb(var(--background-end-rgb)) 40%); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } +@reference "./globals.css"; + +.heading { + @apply text-red-500; } diff --git a/playgrounds/nextjs/app/page.tsx b/playgrounds/nextjs/app/page.tsx index 3c5390442..70ea89898 100644 --- a/playgrounds/nextjs/app/page.tsx +++ b/playgrounds/nextjs/app/page.tsx @@ -1,3 +1,5 @@ +import styles from './page.module.css' + export default function Home() { - return

Hello world!

+ return

Hello world!

} diff --git a/playgrounds/nextjs/package.json b/playgrounds/nextjs/package.json index 8468750dc..5487af5a5 100644 --- a/playgrounds/nextjs/package.json +++ b/playgrounds/nextjs/package.json @@ -5,23 +5,20 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start", - "lint": "next lint" + "start": "next start" }, "dependencies": { "@tailwindcss/postcss": "workspace:^", - "fast-glob": "^3.3.2", - "next": "15.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "fast-glob": "^3.3.3", + "next": "16.2.7", + "react": "19.2.7", + "react-dom": "19.2.7", "tailwindcss": "workspace:^" }, "devDependencies": { "@types/node": "catalog:", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "eslint": "^9.15.0", - "eslint-config-next": "^15.0.3", - "typescript": "^5.5.4" + "@types/react": "19.2.15", + "@types/react-dom": "19.2.3", + "typescript": "^6.0.3" } } diff --git a/playgrounds/nextjs/tsconfig.json b/playgrounds/nextjs/tsconfig.json index 862143548..27414aae7 100644 --- a/playgrounds/nextjs/tsconfig.json +++ b/playgrounds/nextjs/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -20,7 +20,14 @@ "paths": { "@/*": ["./*"], }, + "target": "ES2017", }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + ], "exclude": ["node_modules"], } diff --git a/playgrounds/v3/package.json b/playgrounds/v3/package.json index c69808098..8c08310db 100644 --- a/playgrounds/v3/package.json +++ b/playgrounds/v3/package.json @@ -9,18 +9,16 @@ "upgrade": "node scripts/upgrade.mjs" }, "dependencies": { - "next": "15.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "next": "16.2.7", + "react": "19.2.7", + "react-dom": "19.2.7", "tailwindcss": "^3" }, "devDependencies": { - "@types/node": "^20.14.8", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "autoprefixer": "^10.4.20", - "eslint": "^9.15.0", - "eslint-config-next": "^15.0.3", - "typescript": "^5.5.4" + "@types/node": "catalog:", + "@types/react": "19.2.15", + "@types/react-dom": "19.2.3", + "autoprefixer": "^10.5.0", + "typescript": "^6.0.3" } } diff --git a/playgrounds/v3/tsconfig.json b/playgrounds/v3/tsconfig.json index 862143548..27414aae7 100644 --- a/playgrounds/v3/tsconfig.json +++ b/playgrounds/v3/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -20,7 +20,14 @@ "paths": { "@/*": ["./*"], }, + "target": "ES2017", }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + ], "exclude": ["node_modules"], } diff --git a/playgrounds/vite/package.json b/playgrounds/vite/package.json index 716337c36..25ab1a33b 100644 --- a/playgrounds/vite/package.json +++ b/playgrounds/vite/package.json @@ -10,15 +10,15 @@ }, "dependencies": { "@tailwindcss/vite": "workspace:^", - "@vitejs/plugin-react": "^4.3.1", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "@vitejs/plugin-react": "^6.0.2", + "react": "^19.2.6", + "react-dom": "^19.2.6", "tailwindcss": "workspace:^" }, "devDependencies": { - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "bun": "^1.1.29", + "@types/react": "^19.2.15", + "@types/react-dom": "^19.2.3", + "bun": "^1.3.14", "vite": "catalog:" } } diff --git a/playgrounds/vite/src/app.tsx b/playgrounds/vite/src/app.tsx index 4abc17cb5..8ec502989 100644 --- a/playgrounds/vite/src/app.tsx +++ b/playgrounds/vite/src/app.tsx @@ -2,7 +2,6 @@ export function App() { return (

Hello World

-
) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 440843400..8382b2ba5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,52 +4,138 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@parcel/watcher': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-darwin-arm64': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-darwin-x64': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-linux-arm64-glibc': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-linux-arm64-musl': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-linux-x64-glibc': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-linux-x64-musl': + specifier: 2.6.0 + version: 2.6.0 + '@parcel/watcher-win32-x64': + specifier: 2.6.0 + version: 2.6.0 + '@types/node': + specifier: 22.20.1 + version: 22.20.1 + dedent: + specifier: 1.7.2 + version: 1.7.2 + enhanced-resolve: + specifier: ^5.24.5 + version: 5.24.5 + lightningcss: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-darwin-arm64: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-darwin-x64: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-linux-arm64-gnu: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-linux-arm64-musl: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-linux-x64-gnu: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-linux-x64-musl: + specifier: 1.33.0 + version: 1.33.0 + lightningcss-win32-x64-msvc: + specifier: 1.33.0 + version: 1.33.0 + postcss: + specifier: ^8.5.25 + version: 8.5.25 + prettier: + specifier: 3.9.6 + version: 3.9.6 + vite: + specifier: ^8.2.0 + version: 8.2.0 + webpack: + specifier: ^5.109.2 + version: 5.109.2 + patchedDependencies: - '@parcel/watcher@2.5.0': - hash: pyfayeoxwesjxsefsxp3jpev4q - path: patches/@parcel__watcher@2.5.0.patch - lightningcss@1.26.0: - hash: 5hwfyehqvg5wjb7mwtdvubqbl4 - path: patches/lightningcss@1.26.0.patch + '@napi-rs/cli@3.7.4': 9912bf0a9c2cef8329d11c41fbce4d33ce2bdcf660a630b258c962c0a3c44bed + '@parcel/watcher@2.6.0': 705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da + lightningcss@1.33.0: 1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930 importers: + .: devDependencies: '@playwright/test': - specifier: ^1.49.0 - version: 1.49.0 + specifier: ^1.62.1 + version: 1.62.1 '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 postcss: - specifier: 8.4.49 - version: 8.4.49 + specifier: 'catalog:' + version: 8.5.25 postcss-import: - specifier: ^16.1.0 - version: 16.1.0(postcss@8.4.49) + specifier: ^16.1.1 + version: 16.1.1(postcss@8.5.25) prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: 'catalog:' + version: 3.9.6 prettier-plugin-embed: - specifier: ^0.4.15 - version: 0.4.15 + specifier: ^0.5.1 + version: 0.5.1 prettier-plugin-organize-imports: - specifier: ^4.0.0 - version: 4.0.0(prettier@3.3.3)(typescript@5.5.4) + specifier: ^4.3.0 + version: 4.3.0(prettier@3.9.6)(typescript@5.9.3) tsup: - specifier: ^8.2.4 - version: 8.2.4(jiti@2.4.0)(postcss@8.4.49)(tsx@4.19.1)(typescript@5.5.4)(yaml@2.6.0) + specifier: ^8.5.1 + version: 8.5.1(jiti@2.7.0)(postcss@8.5.25)(typescript@5.9.3)(yaml@2.9.0) turbo: - specifier: ^2.2.3 - version: 2.2.3 + specifier: ^2.10.8 + version: 2.10.8 typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6) + specifier: ^4.1.10 + version: 4.1.10(@types/node@22.20.1)(vite@8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) crates/node: + devDependencies: + '@emnapi/core': + specifier: 1.11.3 + version: 1.11.3 + '@emnapi/runtime': + specifier: 1.11.3 + version: 1.11.3 + '@napi-rs/cli': + specifier: 3.7.4 + version: 3.7.4(patch_hash=9912bf0a9c2cef8329d11c41fbce4d33ce2bdcf660a630b258c962c0a3c44bed)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@25.9.1)(node-addon-api@8.7.0) + '@napi-rs/wasm-runtime': + specifier: ^1.2.2 + version: 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + emnapi: + specifier: 1.11.3 + version: 1.11.3(node-addon-api@8.7.0) optionalDependencies: '@tailwindcss/oxide-android-arm64': specifier: workspace:* @@ -78,16 +164,15 @@ importers: '@tailwindcss/oxide-linux-x64-musl': specifier: workspace:* version: link:npm/linux-x64-musl + '@tailwindcss/oxide-wasm32-wasi': + specifier: workspace:* + version: link:npm/wasm32-wasi '@tailwindcss/oxide-win32-arm64-msvc': specifier: workspace:* version: link:npm/win32-arm64-msvc '@tailwindcss/oxide-win32-x64-msvc': specifier: workspace:* version: link:npm/win32-x64-msvc - devDependencies: - '@napi-rs/cli': - specifier: ^2.18.4 - version: 2.18.4 crates/node/npm/android-arm-eabi: {} @@ -109,6 +194,27 @@ importers: crates/node/npm/linux-x64-musl: {} + crates/node/npm/wasm32-wasi: + dependencies: + '@emnapi/core': + specifier: ^1.11.3 + version: 1.11.3 + '@emnapi/runtime': + specifier: ^1.11.3 + version: 1.11.3 + '@emnapi/wasi-threads': + specifier: ^1.2.3 + version: 1.2.3 + '@napi-rs/wasm-runtime': + specifier: ^1.2.2 + version: 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@tybys/wasm-util': + specifier: ^0.10.3 + version: 0.10.3 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + crates/node/npm/win32-arm64-msvc: {} crates/node/npm/win32-x64-msvc: {} @@ -116,29 +222,44 @@ importers: integrations: devDependencies: dedent: - specifier: 1.5.3 - version: 1.5.3 + specifier: 'catalog:' + version: 1.7.2 fast-glob: - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.3.3 + version: 3.3.3 + source-map-js: + specifier: ^1.2.1 + version: 1.2.1 + yaml: + specifier: ^2.9.0 + version: 2.9.0 + + packages/@tailwindcss-browser: + devDependencies: + h3: + specifier: ^1.15.11 + version: 1.15.11 + listhen: + specifier: ^1.10.1 + version: 1.10.1(@parcel/watcher@2.6.0(patch_hash=705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da)) + tailwindcss: + specifier: workspace:* + version: link:../tailwindcss packages/@tailwindcss-cli: dependencies: '@parcel/watcher': - specifier: ^2.5.0 - version: 2.5.0(patch_hash=pyfayeoxwesjxsefsxp3jpev4q) + specifier: 'catalog:' + version: 2.6.0(patch_hash=705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da) '@tailwindcss/node': - specifier: workspace:^ + specifier: workspace:* version: link:../@tailwindcss-node '@tailwindcss/oxide': - specifier: workspace:^ + specifier: workspace:* version: link:../../crates/node enhanced-resolve: - specifier: ^5.17.1 - version: 5.17.1 - lightningcss: specifier: 'catalog:' - version: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) + version: 5.24.5 mri: specifier: ^1.2.0 version: 1.2.0 @@ -151,12 +272,24 @@ importers: packages/@tailwindcss-node: dependencies: + '@jridgewell/remapping': + specifier: ^2.3.5 + version: 2.3.5 enhanced-resolve: - specifier: ^5.17.1 - version: 5.17.1 + specifier: 'catalog:' + version: 5.24.5 jiti: - specifier: ^2.4.0 - version: 2.4.0 + specifier: ^2.7.0 + version: 2.7.0 + lightningcss: + specifier: 'catalog:' + version: 1.33.0(patch_hash=1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930) + magic-string: + specifier: ^1.1.0 + version: 1.1.0 + source-map-js: + specifier: ^1.2.1 + version: 1.2.1 tailwindcss: specifier: workspace:* version: link:../tailwindcss @@ -167,36 +300,33 @@ importers: specifier: ^5.2.0 version: 5.2.0 '@tailwindcss/node': - specifier: workspace:^ + specifier: workspace:* version: link:../@tailwindcss-node '@tailwindcss/oxide': - specifier: workspace:^ + specifier: workspace:* version: link:../../crates/node - lightningcss: - specifier: 'catalog:' - version: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) postcss: - specifier: ^8.4.41 - version: 8.4.41 + specifier: 'catalog:' + version: 8.5.25 tailwindcss: specifier: workspace:* version: link:../tailwindcss devDependencies: '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 '@types/postcss-import': specifier: 14.0.3 version: 14.0.3 dedent: - specifier: 1.5.3 - version: 1.5.3 + specifier: 'catalog:' + version: 1.7.2 internal-example-plugin: specifier: workspace:* version: link:../internal-example-plugin postcss-import: - specifier: ^16.1.0 - version: 16.1.0(postcss@8.4.41) + specifier: ^16.1.1 + version: 16.1.1(postcss@8.5.25) packages/@tailwindcss-standalone: dependencies: @@ -204,96 +334,115 @@ importers: specifier: ^0.4.2 version: 0.4.2(tailwindcss@packages+tailwindcss) '@tailwindcss/cli': - specifier: workspace:^ + specifier: workspace:* version: link:../@tailwindcss-cli '@tailwindcss/forms': - specifier: ^0.5.9 - version: 0.5.9(tailwindcss@packages+tailwindcss) + specifier: ^0.5.11 + version: 0.5.11(tailwindcss@packages+tailwindcss) '@tailwindcss/typography': - specifier: ^0.5.15 - version: 0.5.15(tailwindcss@packages+tailwindcss) + specifier: ^0.5.20 + version: 0.5.20(tailwindcss@packages+tailwindcss) detect-libc: specifier: 1.0.3 version: 1.0.3 enhanced-resolve: - specifier: ^5.17.1 - version: 5.17.1 + specifier: 'catalog:' + version: 5.24.5 tailwindcss: - specifier: workspace:^ + specifier: workspace:* version: link:../tailwindcss devDependencies: '@parcel/watcher-darwin-arm64': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-darwin-x64': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-linux-arm64-glibc': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-linux-arm64-musl': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-linux-x64-glibc': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-linux-x64-musl': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@parcel/watcher-win32-x64': - specifier: ^2.5.0 - version: 2.5.0 + specifier: 'catalog:' + version: 2.6.0 '@types/bun': - specifier: ^1.1.13 - version: 1.1.13 + specifier: ^1.3.14 + version: 1.3.14 bun: - specifier: 1.1.29 - version: 1.1.29 + specifier: ^1.3.14 + version: 1.3.14 lightningcss-darwin-arm64: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-darwin-x64: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-linux-arm64-gnu: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-linux-arm64-musl: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-linux-x64-gnu: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-linux-x64-musl: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 lightningcss-win32-x64-msvc: - specifier: ^1.25.1 - version: 1.26.0 + specifier: 'catalog:' + version: 1.33.0 + + packages/@tailwindcss-turbopack: + dependencies: + '@alloc/quick-lru': + specifier: ^5.2.0 + version: 5.2.0 + '@tailwindcss/node': + specifier: workspace:* + version: link:../@tailwindcss-node + '@tailwindcss/oxide': + specifier: workspace:* + version: link:../../crates/node + tailwindcss: + specifier: workspace:* + version: link:../tailwindcss + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.20.1 + webpack: + specifier: 'catalog:' + version: 5.109.2(esbuild@0.27.7)(postcss@8.5.25) packages/@tailwindcss-upgrade: dependencies: '@tailwindcss/node': - specifier: workspace:^ + specifier: workspace:* version: link:../@tailwindcss-node '@tailwindcss/oxide': - specifier: workspace:^ + specifier: workspace:* version: link:../../crates/node - braces: - specifier: ^3.0.3 - version: 3.0.3 dedent: - specifier: 1.5.3 - version: 1.5.3 + specifier: 'catalog:' + version: 1.7.2 enhanced-resolve: - specifier: ^5.17.1 - version: 5.17.1 + specifier: 'catalog:' + version: 5.24.5 globby: - specifier: ^14.0.2 - version: 14.0.2 + specifier: ^16.2.2 + version: 16.2.2 jiti: - specifier: ^2.0.0-beta.3 - version: 2.4.0 + specifier: ^2.7.0 + version: 2.7.0 mri: specifier: ^1.2.0 version: 1.2.0 @@ -301,75 +450,109 @@ importers: specifier: ^1.1.1 version: 1.1.1 postcss: - specifier: ^8.4.41 - version: 8.4.41 + specifier: 'catalog:' + version: 8.5.25 postcss-import: - specifier: ^16.1.0 - version: 16.1.0(postcss@8.4.41) + specifier: ^16.1.1 + version: 16.1.1(postcss@8.5.25) postcss-selector-parser: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^7.1.4 + version: 7.1.4 prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: 'catalog:' + version: 3.9.6 + semver: + specifier: ^7.8.5 + version: 7.8.5 tailwindcss: specifier: workspace:* version: link:../tailwindcss tree-sitter: - specifier: ^0.22.1 - version: 0.22.1 + specifier: ^0.25.1 + version: 0.25.1 tree-sitter-typescript: specifier: ^0.23.2 - version: 0.23.2(tree-sitter@0.22.1) + version: 0.23.2(tree-sitter@0.25.1) devDependencies: - '@types/braces': - specifier: ^3.0.4 - version: 3.0.4 '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 '@types/postcss-import': specifier: ^14.0.3 version: 14.0.3 + '@types/semver': + specifier: ^7.8.0 + version: 7.8.0 packages/@tailwindcss-vite: dependencies: '@tailwindcss/node': - specifier: workspace:^ + specifier: workspace:* version: link:../@tailwindcss-node '@tailwindcss/oxide': - specifier: workspace:^ + specifier: workspace:* version: link:../../crates/node - lightningcss: - specifier: 'catalog:' - version: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) tailwindcss: specifier: workspace:* version: link:../tailwindcss devDependencies: '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 vite: specifier: 'catalog:' - version: 6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0) + version: 8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + + packages/@tailwindcss-webpack: + dependencies: + '@alloc/quick-lru': + specifier: ^5.2.0 + version: 5.2.0 + '@rspack/core': + specifier: ^1.0.0 || ^2.0.0 + version: 2.0.2(@swc/helpers@0.5.15) + '@tailwindcss/node': + specifier: workspace:* + version: link:../@tailwindcss-node + '@tailwindcss/oxide': + specifier: workspace:* + version: link:../../crates/node + tailwindcss: + specifier: workspace:* + version: link:../tailwindcss + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.20.1 + webpack: + specifier: 'catalog:' + version: 5.109.2(esbuild@0.27.7)(postcss@8.5.25) packages/internal-example-plugin: {} packages/tailwindcss: devDependencies: + '@jridgewell/remapping': + specifier: ^2.3.5 + version: 2.3.5 '@tailwindcss/oxide': specifier: workspace:^ version: link:../../crates/node '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 dedent: - specifier: 1.5.3 - version: 1.5.3 + specifier: 'catalog:' + version: 1.7.2 lightningcss: specifier: 'catalog:' - version: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) + version: 1.33.0(patch_hash=1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930) + magic-string: + specifier: ^1.1.0 + version: 1.1.0 + source-map-js: + specifier: ^1.2.1 + version: 1.2.1 playgrounds/nextjs: dependencies: @@ -377,76 +560,64 @@ importers: specifier: workspace:^ version: link:../../packages/@tailwindcss-postcss fast-glob: - specifier: ^3.3.2 - version: 3.3.2 + specifier: ^3.3.3 + version: 3.3.3 next: - specifier: 15.0.3 - version: 15.0.3(@playwright/test@1.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 16.2.7 + version: 16.2.7(@playwright/test@1.62.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: 19.2.7 + version: 19.2.7 react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: 19.2.7 + version: 19.2.7(react@19.2.7) tailwindcss: specifier: workspace:^ version: link:../../packages/tailwindcss devDependencies: '@types/node': specifier: 'catalog:' - version: 20.14.13 + version: 22.20.1 '@types/react': - specifier: ^18.3.12 - version: 18.3.12 + specifier: 19.2.15 + version: 19.2.15 '@types/react-dom': - specifier: ^18.3.1 - version: 18.3.1 - eslint: - specifier: ^9.15.0 - version: 9.15.0(jiti@2.4.0) - eslint-config-next: - specifier: ^15.0.3 - version: 15.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.15) typescript: - specifier: ^5.5.4 - version: 5.5.4 + specifier: ^6.0.3 + version: 6.0.3 playgrounds/v3: dependencies: next: - specifier: 15.0.3 - version: 15.0.3(@playwright/test@1.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 16.2.7 + version: 16.2.7(@playwright/test@1.62.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: 19.2.7 + version: 19.2.7 react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: 19.2.7 + version: 19.2.7(react@19.2.7) tailwindcss: specifier: ^3 - version: 3.4.14 + version: 3.4.19(yaml@2.9.0) devDependencies: '@types/node': - specifier: ^20.14.8 - version: 20.14.13 + specifier: 'catalog:' + version: 22.20.1 '@types/react': - specifier: ^18.3.12 - version: 18.3.12 + specifier: 19.2.15 + version: 19.2.15 '@types/react-dom': - specifier: ^18.3.1 - version: 18.3.1 + specifier: 19.2.3 + version: 19.2.3(@types/react@19.2.15) autoprefixer: - specifier: ^10.4.20 - version: 10.4.20(postcss@8.4.47) - eslint: - specifier: ^9.15.0 - version: 9.15.0(jiti@2.4.0) - eslint-config-next: - specifier: ^15.0.3 - version: 15.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) + specifier: ^10.5.0 + version: 10.5.0(postcss@8.5.25) typescript: - specifier: ^5.5.4 - version: 5.6.3 + specifier: ^6.0.3 + version: 6.0.3 playgrounds/vite: dependencies: @@ -454,4194 +625,2306 @@ importers: specifier: workspace:^ version: link:../../packages/@tailwindcss-vite '@vitejs/plugin-react': - specifier: ^4.3.1 - version: 4.3.1(vite@6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0)) + specifier: ^6.0.2 + version: 6.0.2(vite@8.2.0(@types/node@25.9.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) react: - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.2.6 + version: 19.2.6 react-dom: - specifier: ^18.3.1 - version: 18.3.1(react@18.3.1) + specifier: ^19.2.6 + version: 19.2.6(react@19.2.6) tailwindcss: specifier: workspace:^ version: link:../../packages/tailwindcss devDependencies: '@types/react': - specifier: ^18.3.12 - version: 18.3.12 + specifier: ^19.2.15 + version: 19.2.15 '@types/react-dom': - specifier: ^18.3.1 - version: 18.3.1 + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.15) bun: - specifier: ^1.1.29 - version: 1.1.29 + specifier: ^1.3.14 + version: 1.3.14 vite: specifier: 'catalog:' - version: 6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0) + version: 8.2.0(@types/node@25.9.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) packages: + '@alloc/quick-lru@5.2.0': - resolution: - { - integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: - { - integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, - } - engines: { node: '>=6.0.0' } + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@babel/code-frame@7.24.7': - resolution: - { - integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==, - } - engines: { node: '>=6.9.0' } + '@emnapi/core@1.11.3': + resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==} - '@babel/compat-data@7.25.2': - resolution: - { - integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==, - } - engines: { node: '>=6.9.0' } + '@emnapi/core@2.0.0-alpha.3': + resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} - '@babel/core@7.25.2': - resolution: - { - integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==, - } - engines: { node: '>=6.9.0' } + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@babel/generator@7.25.0': - resolution: - { - integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==, - } - engines: { node: '>=6.9.0' } + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} - '@babel/helper-compilation-targets@7.25.2': - resolution: - { - integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==, - } - engines: { node: '>=6.9.0' } + '@emnapi/runtime@2.0.0-alpha.3': + resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} - '@babel/helper-module-imports@7.24.7': - resolution: - { - integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==, - } - engines: { node: '>=6.9.0' } + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@babel/helper-module-transforms@7.25.2': - resolution: - { - integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0 + '@emnapi/wasi-threads@1.2.3': + resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==} - '@babel/helper-plugin-utils@7.24.8': - resolution: - { - integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==, - } - engines: { node: '>=6.9.0' } + '@emnapi/wasi-threads@2.0.1': + resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} - '@babel/helper-simple-access@7.24.7': - resolution: - { - integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==, - } - engines: { node: '>=6.9.0' } - - '@babel/helper-string-parser@7.24.8': - resolution: - { - integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==, - } - engines: { node: '>=6.9.0' } - - '@babel/helper-validator-identifier@7.24.7': - resolution: - { - integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==, - } - engines: { node: '>=6.9.0' } - - '@babel/helper-validator-option@7.24.8': - resolution: - { - integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==, - } - engines: { node: '>=6.9.0' } - - '@babel/helpers@7.25.0': - resolution: - { - integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==, - } - engines: { node: '>=6.9.0' } - - '@babel/highlight@7.24.7': - resolution: - { - integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==, - } - engines: { node: '>=6.9.0' } - - '@babel/parser@7.25.3': - resolution: - { - integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==, - } - engines: { node: '>=6.0.0' } - hasBin: true - - '@babel/plugin-transform-react-jsx-self@7.24.7': - resolution: - { - integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.24.7': - resolution: - { - integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.25.0': - resolution: - { - integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==, - } - engines: { node: '>=6.9.0' } - - '@babel/traverse@7.25.3': - resolution: - { - integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==, - } - engines: { node: '>=6.9.0' } - - '@babel/types@7.25.2': - resolution: - { - integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==, - } - engines: { node: '>=6.9.0' } - - '@emnapi/runtime@1.3.1': - resolution: - { - integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==, - } - - '@esbuild/aix-ppc64@0.21.5': - resolution: - { - integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==, - } - engines: { node: '>=12' } + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.23.0': - resolution: - { - integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.23.1': - resolution: - { - integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.24.0': - resolution: - { - integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.21.5': - resolution: - { - integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==, - } - engines: { node: '>=12' } + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.23.0': - resolution: - { - integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.23.1': - resolution: - { - integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.24.0': - resolution: - { - integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: - { - integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==, - } - engines: { node: '>=12' } + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.23.0': - resolution: - { - integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.23.1': - resolution: - { - integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.24.0': - resolution: - { - integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: - { - integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==, - } - engines: { node: '>=12' } + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.23.0': - resolution: - { - integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.23.1': - resolution: - { - integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.24.0': - resolution: - { - integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.21.5': - resolution: - { - integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==, - } - engines: { node: '>=12' } + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.23.0': - resolution: - { - integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.23.1': - resolution: - { - integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.24.0': - resolution: - { - integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: - { - integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==, - } - engines: { node: '>=12' } + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.23.0': - resolution: - { - integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.23.1': - resolution: - { - integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.24.0': - resolution: - { - integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: - { - integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==, - } - engines: { node: '>=12' } + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.23.0': - resolution: - { - integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.23.1': - resolution: - { - integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.24.0': - resolution: - { - integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: - { - integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==, - } - engines: { node: '>=12' } + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.23.0': - resolution: - { - integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.23.1': - resolution: - { - integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.24.0': - resolution: - { - integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.21.5': - resolution: - { - integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==, - } - engines: { node: '>=12' } + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.23.0': - resolution: - { - integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.23.1': - resolution: - { - integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.24.0': - resolution: - { - integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: - { - integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==, - } - engines: { node: '>=12' } + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.23.0': - resolution: - { - integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.23.1': - resolution: - { - integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.24.0': - resolution: - { - integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==, - } - engines: { node: '>=18' } - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: - { - integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==, - } - engines: { node: '>=12' } + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.23.0': - resolution: - { - integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.23.1': - resolution: - { - integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.24.0': - resolution: - { - integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: - { - integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==, - } - engines: { node: '>=12' } + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.23.0': - resolution: - { - integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==, - } - engines: { node: '>=18' } - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.23.1': - resolution: - { - integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==, - } - engines: { node: '>=18' } - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.24.0': - resolution: - { - integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==, - } - engines: { node: '>=18' } - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: - { - integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==, - } - engines: { node: '>=12' } + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.23.0': - resolution: - { - integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==, - } - engines: { node: '>=18' } - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.23.1': - resolution: - { - integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==, - } - engines: { node: '>=18' } - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.24.0': - resolution: - { - integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==, - } - engines: { node: '>=18' } - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: - { - integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==, - } - engines: { node: '>=12' } + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.23.0': - resolution: - { - integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.23.1': - resolution: - { - integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.24.0': - resolution: - { - integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==, - } - engines: { node: '>=18' } - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: - { - integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==, - } - engines: { node: '>=12' } + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.23.0': - resolution: - { - integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==, - } - engines: { node: '>=18' } - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.23.1': - resolution: - { - integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==, - } - engines: { node: '>=18' } - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.24.0': - resolution: - { - integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==, - } - engines: { node: '>=18' } - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: - { - integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==, - } - engines: { node: '>=12' } + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.23.0': - resolution: - { - integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==, - } - engines: { node: '>=18' } - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.23.1': - resolution: - { - integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==, - } - engines: { node: '>=18' } - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.24.0': - resolution: - { - integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==, - } - engines: { node: '>=18' } - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.21.5': - resolution: - { - integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==, - } - engines: { node: '>=12' } + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.23.0': - resolution: - { - integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [linux] + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] - '@esbuild/linux-x64@0.23.1': - resolution: - { - integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.24.0': - resolution: - { - integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-x64@0.21.5': - resolution: - { - integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==, - } - engines: { node: '>=12' } + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': - resolution: - { - integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.23.1': - resolution: - { - integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.24.0': - resolution: - { - integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.23.0': - resolution: - { - integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==, - } - engines: { node: '>=18' } + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.23.1': - resolution: - { - integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==, - } - engines: { node: '>=18' } + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} cpu: [arm64] - os: [openbsd] + os: [openharmony] - '@esbuild/openbsd-arm64@0.24.0': - resolution: - { - integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: - { - integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==, - } - engines: { node: '>=12' } - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.23.0': - resolution: - { - integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.23.1': - resolution: - { - integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.24.0': - resolution: - { - integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.21.5': - resolution: - { - integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==, - } - engines: { node: '>=12' } + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.23.0': - resolution: - { - integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.23.1': - resolution: - { - integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.24.0': - resolution: - { - integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.21.5': - resolution: - { - integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==, - } - engines: { node: '>=12' } + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.23.0': - resolution: - { - integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.23.1': - resolution: - { - integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.24.0': - resolution: - { - integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==, - } - engines: { node: '>=18' } - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: - { - integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==, - } - engines: { node: '>=12' } + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.23.0': - resolution: - { - integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.23.1': - resolution: - { - integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.24.0': - resolution: - { - integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==, - } - engines: { node: '>=18' } - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: - { - integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==, - } - engines: { node: '>=12' } + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.23.0': - resolution: - { - integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [win32] + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} - '@esbuild/win32-x64@0.23.1': - resolution: - { - integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.24.0': - resolution: - { - integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==, - } - engines: { node: '>=18' } - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.4.0': - resolution: - { - integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.11.1': - resolution: - { - integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - - '@eslint-community/regexpp@4.12.1': - resolution: - { - integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - - '@eslint/config-array@0.19.0': - resolution: - { - integrity: sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@eslint/core@0.9.0': - resolution: - { - integrity: sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@eslint/eslintrc@3.2.0': - resolution: - { - integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@eslint/js@9.15.0': - resolution: - { - integrity: sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@eslint/object-schema@2.1.4': - resolution: - { - integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@eslint/plugin-kit@0.2.3': - resolution: - { - integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - '@humanfs/core@0.19.1': - resolution: - { - integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, - } - engines: { node: '>=18.18.0' } - - '@humanfs/node@0.16.6': - resolution: - { - integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==, - } - engines: { node: '>=18.18.0' } - - '@humanwhocodes/module-importer@1.0.1': - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: '>=12.22' } - - '@humanwhocodes/retry@0.3.1': - resolution: - { - integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==, - } - engines: { node: '>=18.18' } - - '@humanwhocodes/retry@0.4.1': - resolution: - { - integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==, - } - engines: { node: '>=18.18' } - - '@img/sharp-darwin-arm64@0.33.5': - resolution: - { - integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.5': - resolution: - { - integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: - { - integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==, - } + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: - { - integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==, - } + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: - { - integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==, - } + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: - { - integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==, - } + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: - { - integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==, - } + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: - { - integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==, - } + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: - { - integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==, - } + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: - { - integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==, - } + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] - '@img/sharp-linux-arm64@0.33.5': - resolution: - { - integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@img/sharp-linux-arm@0.33.5': - resolution: - { - integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] - '@img/sharp-linux-s390x@0.33.5': - resolution: - { - integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@img/sharp-linux-x64@0.33.5': - resolution: - { - integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: - { - integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: - { - integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] - '@img/sharp-wasm32@0.33.5': - resolution: - { - integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.5': - resolution: - { - integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.5': - resolution: - { - integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - '@isaacs/cliui@8.0.2': - resolution: - { - integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, - } - engines: { node: '>=12' } + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@jridgewell/gen-mapping@0.3.5': - resolution: - { - integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==, - } - engines: { node: '>=6.0.0' } + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: - { - integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==, - } - engines: { node: '>=6.0.0' } + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - '@jridgewell/source-map@0.3.6': - resolution: - { - integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==, - } + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: - { - integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==, - } + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jridgewell/trace-mapping@0.3.25': - resolution: - { - integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==, - } - - '@napi-rs/cli@2.18.4': - resolution: - { - integrity: sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==, - } - engines: { node: '>= 10' } + '@napi-rs/cli@3.7.4': + resolution: {integrity: sha512-idELIUceJ9zPgx/shcMt1fSSsteFG68v56RIXi4qUm7OYuJOt7CoMj2KnHVmbOqXgUHWQU1lCULrtQdZDG4F5A==} + engines: {node: '>= 16'} hasBin: true + peerDependencies: + '@emnapi/runtime': ^1.7.1 + peerDependenciesMeta: + '@emnapi/runtime': + optional: true - '@next/env@15.0.3': - resolution: - { - integrity: sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==, - } + '@napi-rs/cross-toolchain@1.0.3': + resolution: {integrity: sha512-ENPfLe4937bsKVTDA6zdABx4pq9w0tHqRrJHyaGxgaPq03a2Bd1unD5XSKjXJjebsABJ+MjAv1A2OvCgK9yehg==} + peerDependencies: + '@napi-rs/cross-toolchain-arm64-target-aarch64': ^1.0.3 + '@napi-rs/cross-toolchain-arm64-target-armv7': ^1.0.3 + '@napi-rs/cross-toolchain-arm64-target-ppc64le': ^1.0.3 + '@napi-rs/cross-toolchain-arm64-target-s390x': ^1.0.3 + '@napi-rs/cross-toolchain-arm64-target-x86_64': ^1.0.3 + '@napi-rs/cross-toolchain-x64-target-aarch64': ^1.0.3 + '@napi-rs/cross-toolchain-x64-target-armv7': ^1.0.3 + '@napi-rs/cross-toolchain-x64-target-ppc64le': ^1.0.3 + '@napi-rs/cross-toolchain-x64-target-s390x': ^1.0.3 + '@napi-rs/cross-toolchain-x64-target-x86_64': ^1.0.3 + peerDependenciesMeta: + '@napi-rs/cross-toolchain-arm64-target-aarch64': + optional: true + '@napi-rs/cross-toolchain-arm64-target-armv7': + optional: true + '@napi-rs/cross-toolchain-arm64-target-ppc64le': + optional: true + '@napi-rs/cross-toolchain-arm64-target-s390x': + optional: true + '@napi-rs/cross-toolchain-arm64-target-x86_64': + optional: true + '@napi-rs/cross-toolchain-x64-target-aarch64': + optional: true + '@napi-rs/cross-toolchain-x64-target-armv7': + optional: true + '@napi-rs/cross-toolchain-x64-target-ppc64le': + optional: true + '@napi-rs/cross-toolchain-x64-target-s390x': + optional: true + '@napi-rs/cross-toolchain-x64-target-x86_64': + optional: true - '@next/eslint-plugin-next@15.0.3': - resolution: - { - integrity: sha512-3Ln/nHq2V+v8uIaxCR6YfYo7ceRgZNXfTd3yW1ukTaFbO+/I8jNakrjYWODvG9BuR2v5kgVtH/C8r0i11quOgw==, - } + '@napi-rs/lzma-android-arm-eabi@1.4.5': + resolution: {integrity: sha512-Up4gpyw2SacmyKWWEib06GhiDdF+H+CCU0LAV8pnM4aJIDqKKd5LHSlBht83Jut6frkB0vwEPmAkv4NjQ5u//Q==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] - '@next/swc-darwin-arm64@15.0.3': - resolution: - { - integrity: sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-android-arm64@1.4.5': + resolution: {integrity: sha512-uwa8sLlWEzkAM0MWyoZJg0JTD3BkPknvejAFG2acUA1raXM8jLrqujWCdOStisXhqQjZ2nDMp3FV6cs//zjfuQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/lzma-darwin-arm64@1.4.5': + resolution: {integrity: sha512-0Y0TQLQ2xAjVabrMDem1NhIssOZzF/y/dqetc6OT8mD3xMTDtF8u5BqZoX3MyPc9FzpsZw4ksol+w7DsxHrpMA==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.0.3': - resolution: - { - integrity: sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-darwin-x64@1.4.5': + resolution: {integrity: sha512-vR2IUyJY3En+V1wJkwmbGWcYiT8pHloTAWdW4pG24+51GIq+intst6Uf6D/r46citObGZrlX0QvMarOkQeHWpw==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.0.3': - resolution: - { - integrity: sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-freebsd-x64@1.4.5': + resolution: {integrity: sha512-XpnYQC5SVovO35tF0xGkbHYjsS6kqyNCjuaLQ2dbEblFRr5cAZVvsJ/9h7zj/5FluJPJRDojVNxGyRhTp4z2lw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/lzma-linux-arm-gnueabihf@1.4.5': + resolution: {integrity: sha512-ic1ZZMoRfRMwtSwxkyw4zIlbDZGC6davC9r+2oX6x9QiF247BRqqT94qGeL5ZP4Vtz0Hyy7TEViWhx5j6Bpzvw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/lzma-linux-arm64-gnu@1.4.5': + resolution: {integrity: sha512-asEp7FPd7C1Yi6DQb45a3KPHKOFBSfGuJWXcAd4/bL2Fjetb2n/KK2z14yfW8YC/Fv6x3rBM0VAZKmJuz4tysg==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] - '@next/swc-linux-arm64-musl@15.0.3': - resolution: - { - integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-linux-arm64-musl@1.4.5': + resolution: {integrity: sha512-yWjcPDgJ2nIL3KNvi4536dlT/CcCWO0DUyEOlBs/SacG7BeD6IjGh6yYzd3/X1Y3JItCbZoDoLUH8iB1lTXo3w==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] - '@next/swc-linux-x64-gnu@15.0.3': - resolution: - { - integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-linux-ppc64-gnu@1.4.5': + resolution: {integrity: sha512-0XRhKuIU/9ZjT4WDIG/qnX7Xz7mSQHYZo9Gb3MP2gcvBgr6BA4zywQ9k3gmQaPn9ECE+CZg2V7DV7kT+x2pUMQ==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-riscv64-gnu@1.4.5': + resolution: {integrity: sha512-QrqDIPEUUB23GCpyQj/QFyMlr8SGxxyExeZz9OWFnHfb70kXdTLWrHS/hEI1Ru+lSbQ/6xRqeoGyQ4Aqdg+/RA==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-s390x-gnu@1.4.5': + resolution: {integrity: sha512-k8RVM5aMhW86E9H0QXdquwojew4H3SwPxbRVbl49/COJQWCUjGi79X6mYruMnMPEznZinUiT1jgKbFo2A00NdA==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@napi-rs/lzma-linux-x64-gnu@1.4.5': + resolution: {integrity: sha512-6rMtBgnIq2Wcl1rQdZsnM+rtCcVCbws1nF8S2NzaUsVaZv8bjrPiAa0lwg4Eqnn1d9lgwqT+cZgm5m+//K08Kw==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] - '@next/swc-linux-x64-musl@15.0.3': - resolution: - { - integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-linux-x64-musl@1.4.5': + resolution: {integrity: sha512-eiadGBKi7Vd0bCArBUOO/qqRYPHt/VQVvGyYvDFt6C2ZSIjlD+HuOl+2oS1sjf4CFjK4eDIog6EdXnL0NE6iyQ==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] - '@next/swc-win32-arm64-msvc@15.0.3': - resolution: - { - integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-wasm32-wasi@1.4.5': + resolution: {integrity: sha512-+VyHHlr68dvey6fXc2hehw9gHVFIW3TtGF1XkcbAu65qVXsA9D/T+uuoRVqhE+JCyFHFrO0ixRbZDRK1XJt1sA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/lzma-win32-arm64-msvc@1.4.5': + resolution: {integrity: sha512-eewnqvIyyhHi3KaZtBOJXohLvwwN27gfS2G/YDWdfHlbz1jrmfeHAmzMsP5qv8vGB+T80TMHNkro4kYjeh6Deg==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.0.3': - resolution: - { - integrity: sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==, - } - engines: { node: '>= 10' } + '@napi-rs/lzma-win32-ia32-msvc@1.4.5': + resolution: {integrity: sha512-OeacFVRCJOKNU/a0ephUfYZ2Yt+NvaHze/4TgOwJ0J0P4P7X1mHzN+ig9Iyd74aQDXYqc7kaCXA2dpAOcH87Cg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/lzma-win32-x64-msvc@1.4.5': + resolution: {integrity: sha512-T4I1SamdSmtyZgDXGAGP+y5LEK5vxHUFwe8mz6D4R7Sa5/WCxTcCIgPJ9BD7RkpO17lzhlaM2vmVvMy96Lvk9Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/lzma@1.4.5': + resolution: {integrity: sha512-zS5LuN1OBPAyZpda2ZZgYOEDC+xecUdAGnrvbYzjnLXkrq/OBC3B9qcRvlxbDR3k5H/gVfvef1/jyUqPknqjbg==} + engines: {node: '>= 10'} + + '@napi-rs/tar-android-arm-eabi@1.1.0': + resolution: {integrity: sha512-h2Ryndraj/YiKgMV/r5by1cDusluYIRT0CaE0/PekQ4u+Wpy2iUVqvzVU98ZPnhXaNeYxEvVJHNGafpOfaD0TA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/tar-android-arm64@1.1.0': + resolution: {integrity: sha512-DJFyQHr1ZxNZorm/gzc1qBNLF/FcKzcH0V0Vwan5P+o0aE2keQIGEjJ09FudkF9v6uOuJjHCVDdK6S6uHtShAw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/tar-darwin-arm64@1.1.0': + resolution: {integrity: sha512-Zz2sXRzjIX4e532zD6xm2SjXEym6MkvfCvL2RMpG2+UwNVDVscHNcz3d47Pf3sysP2e2af7fBB3TIoK2f6trPw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/tar-darwin-x64@1.1.0': + resolution: {integrity: sha512-EI+CptIMNweT0ms9S3mkP/q+J6FNZ1Q6pvpJOEcWglRfyfQpLqjlC0O+dptruTPE8VamKYuqdjxfqD8hifZDOA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/tar-freebsd-x64@1.1.0': + resolution: {integrity: sha512-J0PIqX+pl6lBIAckL/c87gpodLbjZB1OtIK+RDscKC9NLdpVv6VGOxzUV/fYev/hctcE8EfkLbgFOfpmVQPg2g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/tar-linux-arm-gnueabihf@1.1.0': + resolution: {integrity: sha512-SLgIQo3f3EjkZ82ZwvrEgFvMdDAhsxCYjyoSuWfHCz0U16qx3SuGCp8+FYOPYCECHN3ZlGjXnoAIt9ERd0dEUg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/tar-linux-arm64-gnu@1.1.0': + resolution: {integrity: sha512-d014cdle52EGaH6GpYTQOP9Py7glMO1zz/+ynJPjjzYFSxvdYx0byrjumZk2UQdIyGZiJO2MEFpCkEEKFSgPYA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-arm64-musl@1.1.0': + resolution: {integrity: sha512-L/y1/26q9L/uBqiW/JdOb/Dc94egFvNALUZV2WCGKQXc6UByPBMgdiEyW2dtoYxYYYYc+AKD+jr+wQPcvX2vrQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/tar-linux-ppc64-gnu@1.1.0': + resolution: {integrity: sha512-EPE1K/80RQvPbLRJDJs1QmCIcH+7WRi0F73+oTe1582y9RtfGRuzAkzeBuAGRXAQEjRQw/RjtNqr6UTJ+8UuWQ==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-s390x-gnu@1.1.0': + resolution: {integrity: sha512-B2jhWiB1ffw1nQBqLUP1h4+J1ovAxBOoe5N2IqDMOc63fsPZKNqF1PvO/dIem8z7LL4U4bsfmhy3gBfu547oNQ==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-x64-gnu@1.1.0': + resolution: {integrity: sha512-tbZDHnb9617lTnsDMGo/eAMZxnsQFnaRe+MszRqHguKfMwkisc9CCJnks/r1o84u5fECI+J/HOrKXgczq/3Oww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/tar-linux-x64-musl@1.1.0': + resolution: {integrity: sha512-dV6cODlzbO8u6Anmv2N/ilQHq/AWz0xyltuXoLU3yUyXbZcnWYZuB2rL8OBGPmqNcD+x9NdScBNXh7vWN0naSQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/tar-wasm32-wasi@1.1.0': + resolution: {integrity: sha512-jIa9nb2HzOrfH0F8QQ9g3WE4aMH5vSI5/1NYVNm9ysCmNjCCtMXCAhlI3WKCdm/DwHf0zLqdrrtDFXODcNaqMw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/tar-win32-arm64-msvc@1.1.0': + resolution: {integrity: sha512-vfpG71OB0ijtjemp3WTdmBKJm9R70KM8vsSExMsIQtV0lVzP07oM1CW6JbNRPXNLhRoue9ofYLiUDk8bE0Hckg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/tar-win32-ia32-msvc@1.1.0': + resolution: {integrity: sha512-hGPyPW60YSpOSgzfy68DLBHgi6HxkAM+L59ZZZPMQ0TOXjQg+p2EW87+TjZfJOkSpbYiEkULwa/f4a2hcVjsqQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/tar-win32-x64-msvc@1.1.0': + resolution: {integrity: sha512-L6Ed1DxXK9YSCMyvpR8MiNAyKNkQLjsHsHK9E0qnHa8NzLFqzDKhvs5LfnWxM2kJ+F7m/e5n9zPm24kHb3LsVw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/tar@1.1.0': + resolution: {integrity: sha512-7cmzIu+Vbupriudo7UudoMRH2OA3cTw67vva8MxeoAe5S7vPFI7z0vp0pMXiA25S8IUJefImQ90FeJjl8fjEaQ==} + engines: {node: '>= 10'} + + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@napi-rs/wasm-runtime@1.2.2': + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 + + '@napi-rs/wasm-tools-android-arm-eabi@1.0.1': + resolution: {integrity: sha512-lr07E/l571Gft5v4aA1dI8koJEmF1F0UigBbsqg9OWNzg80H3lDPO+auv85y3T/NHE3GirDk7x/D3sLO57vayw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/wasm-tools-android-arm64@1.0.1': + resolution: {integrity: sha512-WDR7S+aRLV6LtBJAg5fmjKkTZIdrEnnQxgdsb7Cf8pYiMWBHLU+LC49OUVppQ2YSPY0+GeYm9yuZWW3kLjJ7Bg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/wasm-tools-darwin-arm64@1.0.1': + resolution: {integrity: sha512-qWTI+EEkiN0oIn/N2gQo7+TVYil+AJ20jjuzD2vATS6uIjVz+Updeqmszi7zq7rdFTLp6Ea3/z4kDKIfZwmR9g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/wasm-tools-darwin-x64@1.0.1': + resolution: {integrity: sha512-bA6hubqtHROR5UI3tToAF/c6TDmaAgF0SWgo4rADHtQ4wdn0JeogvOk50gs2TYVhKPE2ZD2+qqt7oBKB+sxW3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/wasm-tools-freebsd-x64@1.0.1': + resolution: {integrity: sha512-90+KLBkD9hZEjPQW1MDfwSt5J1L46EUKacpCZWyRuL6iIEO5CgWU0V/JnEgFsDOGyyYtiTvHc5bUdUTWd4I9Vg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/wasm-tools-linux-arm64-gnu@1.0.1': + resolution: {integrity: sha512-rG0QlS65x9K/u3HrKafDf8cFKj5wV2JHGfl8abWgKew0GVPyp6vfsDweOwHbWAjcHtp2LHi6JHoW80/MTHm52Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/wasm-tools-linux-arm64-musl@1.0.1': + resolution: {integrity: sha512-jAasbIvjZXCgX0TCuEFQr+4D6Lla/3AAVx2LmDuMjgG4xoIXzjKWl7c4chuaD+TI+prWT0X6LJcdzFT+ROKGHQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/wasm-tools-linux-x64-gnu@1.0.1': + resolution: {integrity: sha512-Plgk5rPqqK2nocBGajkMVbGm010Z7dnUgq0wtnYRZbzWWxwWcXfZMPa8EYxrK4eE8SzpI7VlZP1tdVsdjgGwMw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/wasm-tools-linux-x64-musl@1.0.1': + resolution: {integrity: sha512-GW7AzGuWxtQkyHknHWYFdR0CHmW6is8rG2Rf4V6GNmMpmwtXt/ItWYWtBe4zqJWycMNazpfZKSw/BpT7/MVCXQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/wasm-tools-wasm32-wasi@1.0.1': + resolution: {integrity: sha512-/nQVSTrqSsn7YdAc2R7Ips/tnw5SPUcl3D7QrXCNGPqjbatIspnaexvaOYNyKMU6xPu+pc0BTnKVmqhlJJCPLA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@napi-rs/wasm-tools-win32-arm64-msvc@1.0.1': + resolution: {integrity: sha512-PFi7oJIBu5w7Qzh3dwFea3sHRO3pojMsaEnUIy22QvsW+UJfNQwJCryVrpoUt8m4QyZXI+saEq/0r4GwdoHYFQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/wasm-tools-win32-ia32-msvc@1.0.1': + resolution: {integrity: sha512-gXkuYzxQsgkj05Zaq+KQTkHIN83dFAwMcTKa2aQcpYPRImFm2AQzEyLtpXmyCWzJ0F9ZYAOmbSyrNew8/us6bw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/wasm-tools-win32-x64-msvc@1.0.1': + resolution: {integrity: sha512-rEAf05nol3e3eei2sRButmgXP+6ATgm0/38MKhz9Isne82T4rPIMYsCIFj0kOisaGeVwoi2fnm7O9oWp5YVnYQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/wasm-tools@1.0.1': + resolution: {integrity: sha512-enkZYyuCdo+9jneCPE/0fjIta4wWnvVN9hBo2HuiMpRF0q3lzv1J6b/cl7i0mxZUKhBrV3aCKDBQnCOhwKbPmQ==} + engines: {node: '>= 10'} + + '@next/env@16.2.7': + resolution: {integrity: sha512-tMJizPlj6ZYpBMMdK8S0LJufrP4QTdR6pcv9KQ/bVETPAmg0j1mlHE9G2c38UyGHxoBapgwuj7XjbGJ2RcDFOg==} + + '@next/swc-darwin-arm64@16.2.7': + resolution: {integrity: sha512-vm1EDI/pVaBNNiychmxk3fft+OhQPVD9cIM/tReLZIQ3TfQ4kqI9DwKk00dzuS1ulC7icbrzCFrmRRlk9PfNdw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.2.7': + resolution: {integrity: sha512-O3IRSv1ZBL1zs0WrIgefTEcTKFVn+ryxBNe54erJ6KsD+2f/Mmt7g2jOYh8PSBdUwPtKQJuCsTMlZ7tIu2AcsQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.2.7': + resolution: {integrity: sha512-Re6PZtjBDd0aMU+VcZcC/PrIvj4WhrjDYtMhhCVQamWN4L90EVP0pcEOBQD25prSlw7OzNw5QpHLWMilRLsRNw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.2.7': + resolution: {integrity: sha512-qyogG9QtBzWxgJfeGBvOEHI3851gTfCF3wLZ5RDLTBJGAmE9p1qDwKCOdrBrvBzRvYDT+gUDp72pzlSEfAXgNA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.2.7': + resolution: {integrity: sha512-Vhe4ZDuBpmMogrGi5D4R2Kq4JAQlj6+wvgaFYy31zfES0zPmt6TLA+cuYpM/OLrPZjo2MYQTHVqNUSCR6+fDZQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.2.7': + resolution: {integrity: sha512-srvian89JahFLw1YLBEuhvPJ0DO5lpUeJQMXy4xYo7g628ZlNgXdNkqoxSAv9OYrBfByh6vxISMwW/mRbzCY+g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.2.7': + resolution: {integrity: sha512-GX3wvLpULFuRFJzwHaKfm7QZJ18F4ZSuxlPJ96BoBglCzBmdSjyeBKF+ZhWhvL/ckxNfLnNa7bsObO2ipYpszw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.2.7': + resolution: {integrity: sha512-J4WlM72NMk076Qsg0jTdK3SNXatlSdnjW7L7oNGLst1tAGjHrJh/FYi+pw9wyIjEtGRKDNzD0zuiY16oWYWVaw==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] '@nodelib/fs.scandir@2.1.5': - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} - '@nolyfill/is-core-module@1.0.39': - resolution: - { - integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==, - } - engines: { node: '>=12.4.0' } + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} - '@oven/bun-darwin-aarch64@1.1.29': - resolution: - { - integrity: sha512-Z8WnrXoAXg6ENU6z+Mil94oipMBMliJIj4XWWqjNC33/ZGPqMdPT5MbFQjbdHjxe5bRBW/aHaMU3G35fGx9Seg==, - } + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@6.0.0': + resolution: {integrity: sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.9': + resolution: {integrity: sha512-o8Bi3f608eyM+7BmBiUWxFsdjLb3/ym1cQek5LZOv9KkZcxRrHCPhhRzm6xjO6HVZ85ItD6+sTsjxo821SVa/A==} + engines: {node: '>= 20'} + + '@octokit/rest@22.0.1': + resolution: {integrity: sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==} + engines: {node: '>= 20'} + + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@oven/bun-darwin-aarch64@1.3.14': + resolution: {integrity: sha512-Omj20SuiHBOUjUBIyqtkNjSUIjOtEOJwmbix/ZyFH4BaQ6OZTaaRWIR4TjHVz0yadHgli6lLTiAh1uarnvD49A==} cpu: [arm64] os: [darwin] - '@oven/bun-darwin-x64-baseline@1.1.29': - resolution: - { - integrity: sha512-B+zKlJR+3ANPDU+vNjvXaqgB63hYcCk16/k45hiInvIsDXfSdDn4+LMHwZTFdfUWTCGbNMFg9u4bZCtK0nhMDw==, - } + '@oven/bun-darwin-x64-baseline@1.3.14': + resolution: {integrity: sha512-OSfsTZstc898HHElhU4NccaBGOSSDn5VfahiVTnidZ9B/+wb7WTyfZJaBeJcfjwJ9H2W9uTh2TGtl3UfcXgV9g==} cpu: [x64] os: [darwin] - '@oven/bun-darwin-x64@1.1.29': - resolution: - { - integrity: sha512-tDHDR+OWLCKEP0xE8IGCWRxSsxO5tr3rvyrSzy6CXYkoWPz66kODbtbGfWl8HSwunrQxJKnE2L9LWX2Eiz3Cpw==, - } + '@oven/bun-darwin-x64@1.3.14': + resolution: {integrity: sha512-FFj3QdU/OhlDyZOJ8CWfN5eWLpRlT4qjZg7lMQi7jA6GuoY5ajlO1zWLP/MuHYRSbXQUvV52RejNi8DVnAp13w==} cpu: [x64] os: [darwin] - '@oven/bun-linux-aarch64@1.1.29': - resolution: - { - integrity: sha512-BuV2Gni+FaMo1r7+3vLjefQYtQcWE01zJJ4zCjyzikP1L4r6PUaLeJyKXbqF6sIR0cLjG6LWj66EGskakDyvWA==, - } + '@oven/bun-freebsd-aarch64@1.3.14': + resolution: {integrity: sha512-LIKrXaFxAHybVO5Pf+9XP2FHUj/5APvXTUKk9dqHm5iFz4oH+W24cmhjkJirNujh9hKeTyrpWSe3no9JZKowIw==} cpu: [arm64] - os: [linux] + os: [freebsd] - '@oven/bun-linux-x64-baseline@1.1.29': - resolution: - { - integrity: sha512-tQhm1SDyymBvc2L3zIN7FI+sI3g9gxavDYuvL/Lz17nEk1oqP5DFE2lF9QWJMDdQL3XQKH/drI27yNpfPHgAPw==, - } - cpu: [x64] - os: [linux] - - '@oven/bun-linux-x64@1.1.29': - resolution: - { - integrity: sha512-JVLiTafybuOFIeC80mZEzHdkOCCzlOtOV5zCbvYAIb7D3SM64fNBwrR0dvDkJTQtsjbwt8YeVFN2YRSI/wlFkw==, - } - cpu: [x64] - os: [linux] - - '@oven/bun-windows-x64-baseline@1.1.29': - resolution: - { - integrity: sha512-+QWT8Kp+3Sl54QUa6uBsDzQlX11thMMVAw+yUwSRU4Y5FVSN8RPuxhN8ijJ1QGm1KOYA2sCI6V+3lyNMNmRvAA==, - } - cpu: [x64] - os: [win32] - - '@oven/bun-windows-x64@1.1.29': - resolution: - { - integrity: sha512-d930+pSkNVHPJJBryIh1gsAKk7lp1HzcktFe0bfxwmpHwjj4BiVfMjU1YaCnsdWKNs2Nv1Y1PLVDNyUCQVfo0Q==, - } - cpu: [x64] - os: [win32] - - '@parcel/watcher-android-arm64@2.5.0': - resolution: - { - integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==, - } - engines: { node: '>= 10.0.0' } - cpu: [arm64] - os: [android] - - '@parcel/watcher-darwin-arm64@2.5.0': - resolution: - { - integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==, - } - engines: { node: '>= 10.0.0' } - cpu: [arm64] - os: [darwin] - - '@parcel/watcher-darwin-x64@2.5.0': - resolution: - { - integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==, - } - engines: { node: '>= 10.0.0' } - cpu: [x64] - os: [darwin] - - '@parcel/watcher-freebsd-x64@2.5.0': - resolution: - { - integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==, - } - engines: { node: '>= 10.0.0' } + '@oven/bun-freebsd-x64@1.3.14': + resolution: {integrity: sha512-uwD+fGUH1ADpIF3B1U2jWzzb20QwRLZfj5QZ28GUCGrAJ/nTmWrD6YYGsblCY1wuhldRez3lU40AyuvSCyLYmw==} cpu: [x64] os: [freebsd] - '@parcel/watcher-linux-arm-glibc@2.5.0': - resolution: - { - integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==, - } - engines: { node: '>= 10.0.0' } + '@oven/bun-linux-aarch64-android@1.3.14': + resolution: {integrity: sha512-y4kq5b85lsrmFb9Xvi4w9mA5IEFJkLMrSmYn06q24KjL9rUWDWO3VFZEtteZxUN5+ec3Zm5S8OnJw1umaCbVjA==} + cpu: [arm64] + os: [android] + + '@oven/bun-linux-aarch64-musl@1.3.14': + resolution: {integrity: sha512-jmqOA92Cd1NL/1XBd4bFkJLxQ86K0RW7ohxS2qzzAvuitO4JiIxjjTeCspoU44zCozH72HpfZfUE2On31OjnWA==} + cpu: [arm64] + os: [linux] + + '@oven/bun-linux-aarch64@1.3.14': + resolution: {integrity: sha512-X5SsPZHs+iYO8R/efIcRtc7gT2Q2DgPfliCxEkx4cXBumwkw0c/EsHMNwH3EgGpCDaZ7IYVPhpCG/xBOQHEwZw==} + cpu: [arm64] + os: [linux] + + '@oven/bun-linux-x64-android@1.3.14': + resolution: {integrity: sha512-qe9e1d+3VAEU7nAA2ol9Jvmy/o99PVMSgZhHn7Q/9O3YcDrfEqyQ8zm4zoe5qTEo8HZH0dN03Le0Ys2eQPs7eg==} + cpu: [x64] + os: [android] + + '@oven/bun-linux-x64-baseline@1.3.14': + resolution: {integrity: sha512-q/8EdOC0yUE8FPeoOVq8/Pw5I9/tJaYmUfO/uDUAREx8IUnOJH1RJ5A3BjFqre8pvJoiZA9AovPJq5FnNNjSxA==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64-musl-baseline@1.3.14': + resolution: {integrity: sha512-n6iE71G4lQE4XkrZhQQcL5YUlxDbnq6nqV7zeQi33PMsLT/0kYE+RvHOtBWZ3w0wMdXZfINmp63hIb9ijUBGtw==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64-musl@1.3.14': + resolution: {integrity: sha512-GBCB/k/sIqcr06eTNgg7g46qiUv35Jasx4XiccJ/n7RGqrE4RWUD/XJBbWFprVPjvqd59+QtSnS99XGqvftHfg==} + cpu: [x64] + os: [linux] + + '@oven/bun-linux-x64@1.3.14': + resolution: {integrity: sha512-7OVTAKvwfPmSbIV1HpdOoVVx5VRc427GuPPne93N6vk4eQBPId9nXmZDh9/zGaKPdbVjVtQSZafWQoUjx38Utw==} + cpu: [x64] + os: [linux] + + '@oven/bun-windows-aarch64@1.3.14': + resolution: {integrity: sha512-T7s3x/BsVKQObGU6QDkZeI6wKynzqGbBH1yI77jrrj5siElclxr3DQrDIk8CV4G5/SJq2HHq4kpLyYY2DKCSmA==} + cpu: [arm64] + os: [win32] + + '@oven/bun-windows-x64-baseline@1.3.14': + resolution: {integrity: sha512-uIjLUC1S9DWgICzuoMba7vurBJnBruE4S5CxnvmZkdqWVXRzx1Rgu636HoH+k0qeaQCFh3jeG3JQ1y6fRHv0sw==} + cpu: [x64] + os: [win32] + + '@oven/bun-windows-x64@1.3.14': + resolution: {integrity: sha512-mUFWL3BoYkNpjd8e9PqROiFF/1Xeotq20mABJsiQH62jM1g5zqWh4khw1RZ6bX8Q8fWvlPaxG1PjofkmjUi3vg==} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} + + '@parcel/watcher-android-arm64@2.6.0': + resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.6.0': + resolution: {integrity: sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.6.0': + resolution: {integrity: sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.6.0': + resolution: {integrity: sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.6.0': + resolution: {integrity: sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==} + engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [glibc] - '@parcel/watcher-linux-arm-musl@2.5.0': - resolution: - { - integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-linux-arm-musl@2.6.0': + resolution: {integrity: sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==} + engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] + libc: [musl] - '@parcel/watcher-linux-arm64-glibc@2.5.0': - resolution: - { - integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-linux-arm64-glibc@2.6.0': + resolution: {integrity: sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-arm64-musl@2.5.0': - resolution: - { - integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-linux-arm64-musl@2.6.0': + resolution: {integrity: sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-x64-glibc@2.5.0': - resolution: - { - integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-linux-x64-glibc@2.6.0': + resolution: {integrity: sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-linux-x64-musl@2.5.0': - resolution: - { - integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-linux-x64-musl@2.6.0': + resolution: {integrity: sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-win32-arm64@2.5.0': - resolution: - { - integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-wasm@2.5.6': + resolution: {integrity: sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==} + engines: {node: '>= 10.0.0'} + bundledDependencies: + - napi-wasm + + '@parcel/watcher-win32-arm64@2.6.0': + resolution: {integrity: sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - '@parcel/watcher-win32-ia32@2.5.0': - resolution: - { - integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==, - } - engines: { node: '>= 10.0.0' } - cpu: [ia32] - os: [win32] - - '@parcel/watcher-win32-x64@2.5.0': - resolution: - { - integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher-win32-x64@2.6.0': + resolution: {integrity: sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - '@parcel/watcher@2.5.0': - resolution: - { - integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==, - } - engines: { node: '>= 10.0.0' } + '@parcel/watcher@2.6.0': + resolution: {integrity: sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==} + engines: {node: '>= 10.0.0'} - '@pkgjs/parseargs@0.11.0': - resolution: - { - integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, - } - engines: { node: '>=14' } - - '@playwright/test@1.49.0': - resolution: - { - integrity: sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==, - } - engines: { node: '>=18' } + '@playwright/test@1.62.1': + resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==} + engines: {node: '>=20'} hasBin: true - '@rollup/rollup-android-arm-eabi@4.20.0': - resolution: - { - integrity: sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==, - } - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm-eabi@4.27.4': - resolution: - { - integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==, - } - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.20.0': - resolution: - { - integrity: sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==, - } + '@rolldown/binding-android-arm64@1.2.1': + resolution: {integrity: sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.27.4': - resolution: - { - integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==, - } - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.20.0': - resolution: - { - integrity: sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==, - } + '@rolldown/binding-darwin-arm64@1.2.1': + resolution: {integrity: sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.27.4': - resolution: - { - integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==, - } - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.20.0': - resolution: - { - integrity: sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==, - } + '@rolldown/binding-darwin-x64@1.2.1': + resolution: {integrity: sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.27.4': - resolution: - { - integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==, - } - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.27.4': - resolution: - { - integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==, - } - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.27.4': - resolution: - { - integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==, - } + '@rolldown/binding-freebsd-x64@1.2.1': + resolution: {integrity: sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': - resolution: - { - integrity: sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==, - } + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': + resolution: {integrity: sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.27.4': - resolution: - { - integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==, - } - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.20.0': - resolution: - { - integrity: sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==, - } - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.27.4': - resolution: - { - integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==, - } - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.20.0': - resolution: - { - integrity: sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==, - } + '@rolldown/binding-linux-arm64-gnu@1.2.1': + resolution: {integrity: sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.27.4': - resolution: - { - integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==, - } + '@rolldown/binding-linux-arm64-musl@1.2.1': + resolution: {integrity: sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.20.0': - resolution: - { - integrity: sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==, - } - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.27.4': - resolution: - { - integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==, - } - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': - resolution: - { - integrity: sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==, - } + '@rolldown/binding-linux-ppc64-gnu@1.2.1': + resolution: {integrity: sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-powerpc64le-gnu@4.27.4': - resolution: - { - integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==, - } - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.20.0': - resolution: - { - integrity: sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==, - } - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.27.4': - resolution: - { - integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==, - } - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.20.0': - resolution: - { - integrity: sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==, - } + '@rolldown/binding-linux-s390x-gnu@1.2.1': + resolution: {integrity: sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.27.4': - resolution: - { - integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==, - } - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.20.0': - resolution: - { - integrity: sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==, - } + '@rolldown/binding-linux-x64-gnu@1.2.1': + resolution: {integrity: sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.27.4': - resolution: - { - integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==, - } + '@rolldown/binding-linux-x64-musl@1.2.1': + resolution: {integrity: sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-x64-musl@4.20.0': - resolution: - { - integrity: sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==, - } - cpu: [x64] - os: [linux] + '@rolldown/binding-openharmony-arm64@1.2.1': + resolution: {integrity: sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] - '@rollup/rollup-linux-x64-musl@4.27.4': - resolution: - { - integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==, - } - cpu: [x64] - os: [linux] + '@rolldown/binding-wasm32-wasi@1.2.1': + resolution: {integrity: sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} - '@rollup/rollup-win32-arm64-msvc@4.20.0': - resolution: - { - integrity: sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==, - } + '@rolldown/binding-win32-arm64-msvc@1.2.1': + resolution: {integrity: sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.27.4': - resolution: - { - integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==, - } + '@rolldown/binding-win32-x64-msvc@1.2.1': + resolution: {integrity: sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.20.0': - resolution: - { - integrity: sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==, - } + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.27.4': - resolution: - { - integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==, - } + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@rspack/binding-darwin-arm64@2.0.2': + resolution: {integrity: sha512-0o7lbgBBsDlICWdjIH0q3e0BsSco4GRiImHWVfZSVEG+q2+ykZJvSvYCVhPM1Co375Z0S3VMPa/8SjcY1FHwlw==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@2.0.2': + resolution: {integrity: sha512-tOwxZpoPlTlRs/w6UyUinXJ4TYRVHMlR7+eQxO1R3muKpixvhXQjtvoaY16HuFyTVky5F0IfOoWr3x9FEsgdLg==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@2.0.2': + resolution: {integrity: sha512-1ZD4YFhG1rmgqj+W8hfwHyKV8xDxGsc/3KgU0FwmiVEX7JfzhCkgBO/xlCG79kRKSrzuVzt4icO/G3cCKn0pag==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rspack/binding-linux-arm64-musl@2.0.2': + resolution: {integrity: sha512-/PtTkM/DsDLjeuXTmeJeRfbjCDbcL9jvoVgZrgxYFZ28y2cdLvbChbW9uigOzs5dQEs1CIBQXMTTj7KhdBTuQg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rspack/binding-linux-x64-gnu@2.0.2': + resolution: {integrity: sha512-bBjsZxMHRaPo6X9SokApm6ucs+UhXtAJFyJJyuk2BH4XJsLeCU9Dz1vMwioeohFbJUUeTASVPm6/BL+RhSaunw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rspack/binding-linux-x64-musl@2.0.2': + resolution: {integrity: sha512-HjlpInqzabDNkhVsUJpsHPqa9QYVWBViJoyWNjzXCAW0vKMDvwaphyUvokSinX8FGTlZi/sr5UEaHJo6XtQ35g==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rspack/binding-wasm32-wasi@2.0.2': + resolution: {integrity: sha512-YaRYNFLJRpkGfYjSWR7n9f+nQKtrlmrrffpAn/blc2geHcRvXoBc5SCs1idPtsLhj7H9qWWhs7ucjyHy4csWFg==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@2.0.2': + resolution: {integrity: sha512-d/3kTEKq+asLjRFPO96t+wfWiM7DLN76VQEPDD9bc1kdsZXlVJBuvyXfsgK8bbEvKplWXYcSsokhmEnuXrLOpg==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@2.0.2': + resolution: {integrity: sha512-161cWineq3RW+Jdm1FAfSpXeUtYWvhB3kAbm46vNT9h/YYz+spwsFMvveAZ1nsVSVL0IC5lDBGUte7yUAY8K2g==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.20.0': - resolution: - { - integrity: sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==, - } + '@rspack/binding-win32-x64-msvc@2.0.2': + resolution: {integrity: sha512-y7Q0S1FE+OlkL5GMqLG0PwxrPw6E1r892KhGrGKE1Vdufe5YTEx6xTPxzZ+b7N2KPD7s9G1/iJmWHQxb1+Bjkg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.27.4': - resolution: - { - integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==, - } - cpu: [x64] - os: [win32] + '@rspack/binding@2.0.2': + resolution: {integrity: sha512-0kZPplW9GWx8mfC6DfsaRY3QBIYPuUs42JfmSM6aSb8tMHZAXQeLeMB8M+h8i4SeI+aFtCgO6UuYGtyWf7+L+A==} - '@rtsao/scc@1.1.0': - resolution: - { - integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==, - } + '@rspack/core@2.0.2': + resolution: {integrity: sha512-VM3UHOo26uC+4QSqY5tU1ybI7KuXY5rTof8nhFOaBY9SYau0Smvr+hMSAPmrmHwknB6dXT8yaNVxrj7I+qxE1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@module-federation/runtime-tools': + optional: true + '@swc/helpers': + optional: true - '@rushstack/eslint-patch@1.10.4': - resolution: - { - integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==, - } + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} - '@sindresorhus/merge-streams@2.3.0': - resolution: - { - integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==, - } - engines: { node: '>=18' } + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@swc/counter@0.1.3': - resolution: - { - integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==, - } - - '@swc/helpers@0.5.13': - resolution: - { - integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==, - } + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@tailwindcss/aspect-ratio@0.4.2': - resolution: - { - integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==, - } + resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==} peerDependencies: tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' - '@tailwindcss/forms@0.5.9': - resolution: - { - integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==, - } + '@tailwindcss/forms@0.5.11': + resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==} peerDependencies: - tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20' + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' - '@tailwindcss/typography@0.5.15': - resolution: - { - integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==, - } + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' - '@types/babel__core@7.20.5': - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } + '@turbo/darwin-64@2.10.8': + resolution: {integrity: sha512-po+7rfJfUnFXjWlcoN2RwhErgzCdRtBc1T26vYPcywHlggmCQiQe1uWaE4j+BibI2uY9/2pDoFzMN0rmSaPFOw==} + cpu: [x64] + os: [darwin] - '@types/babel__generator@7.6.8': - resolution: - { - integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==, - } + '@turbo/darwin-arm64@2.10.8': + resolution: {integrity: sha512-+zB2btDJ00lnPRuqOvpVvgl4x34k/djZQGZTTCfjn7JgNCl8QFY5Njo5+dqkY1g/+9gbbsnAvWm9CmJg9ebcXA==} + cpu: [arm64] + os: [darwin] - '@types/babel__template@7.4.4': - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } + '@turbo/linux-64@2.10.8': + resolution: {integrity: sha512-K1dxqiVisyN7cViVsfQLs6xscQbYuI8aO2nbUhFURDACgEDfZRdP/b4CCxeosBJpcMfhYyiibWqJorCnvz9kKg==} + cpu: [x64] + os: [android, linux] - '@types/babel__traverse@7.20.6': - resolution: - { - integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==, - } + '@turbo/linux-arm64@2.10.8': + resolution: {integrity: sha512-Gi77ibVnrE1fEmvr+/wBD/yvRqhwp/RQuCp2+//lv1U1wNFFyVg0V7Wj8FG9FXPFAw5QHReo8rxc9+wBSDZjzA==} + cpu: [arm64] + os: [android, linux] - '@types/braces@3.0.4': - resolution: - { - integrity: sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==, - } + '@turbo/windows-64@2.10.8': + resolution: {integrity: sha512-znnLO1haJPYTHoKMKwlAvlkjRiYbbhBzME6wIGaMd+fwir23U6jVd1ecaTWWi1fbnRVqxMfgDBKseQ/hLKb83g==} + cpu: [x64] + os: [win32] - '@types/bun@1.1.13': - resolution: - { - integrity: sha512-KmQxSBgVWCl6RSuerlLGZlIWfdxkKqat0nxN61+qu4y1KDn0Ll3j7v1Pl8GnaL3a/U6GGWVTJh75ap62kR1E8Q==, - } + '@turbo/windows-arm64@2.10.8': + resolution: {integrity: sha512-VN30vh3b3Czh2WzYHNTfF1FE0YMZ5aHsLO8dBMGHJewA6792wX6iJR8ZxlzFW6WdOu0gEAKIvlYhfyT81Wkm4Q==} + cpu: [arm64] + os: [win32] - '@types/estree@1.0.5': - resolution: - { - integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==, - } + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/estree@1.0.6': - resolution: - { - integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==, - } + '@types/bun@1.3.14': + resolution: {integrity: sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/json-schema@7.0.15': - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': - resolution: - { - integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==, - } + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} - '@types/node@20.12.14': - resolution: - { - integrity: sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==, - } - - '@types/node@20.14.13': - resolution: - { - integrity: sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==, - } + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/postcss-import@14.0.3': - resolution: - { - integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==, - } + resolution: {integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==} - '@types/prop-types@15.7.13': - resolution: - { - integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==, - } - - '@types/react-dom@18.3.1': - resolution: - { - integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==, - } - - '@types/react@18.3.12': - resolution: - { - integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==, - } - - '@types/ws@8.5.12': - resolution: - { - integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==, - } - - '@typescript-eslint/eslint-plugin@8.11.0': - resolution: - { - integrity: sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' + '@types/react': ^19.2.0 + + '@types/react@19.2.15': + resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==} + + '@types/semver@7.8.0': + resolution: {integrity: sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==} + + '@vitejs/plugin-react@6.0.2': + resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 peerDependenciesMeta: - typescript: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: optional: true - '@typescript-eslint/parser@8.11.0': - resolution: - { - integrity: sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - typescript: + msw: + optional: true + vite: optional: true - '@typescript-eslint/scope-manager@8.11.0': - resolution: - { - integrity: sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} - '@typescript-eslint/type-utils@8.11.0': - resolution: - { - integrity: sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} - '@typescript-eslint/types@8.11.0': - resolution: - { - integrity: sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} - '@typescript-eslint/typescript-estree@8.11.0': - resolution: - { - integrity: sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} - '@typescript-eslint/utils@8.11.0': - resolution: - { - integrity: sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} - '@typescript-eslint/visitor-keys@8.11.0': - resolution: - { - integrity: sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - '@vitejs/plugin-react@4.3.1': - resolution: - { - integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==, - } - engines: { node: ^14.18.0 || >=16.0.0 } - peerDependencies: - vite: ^4.2.0 || ^5.0.0 + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - '@vitest/expect@2.0.5': - resolution: - { - integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==, - } + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - '@vitest/pretty-format@2.0.5': - resolution: - { - integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==, - } + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - '@vitest/runner@2.0.5': - resolution: - { - integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==, - } + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - '@vitest/snapshot@2.0.5': - resolution: - { - integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==, - } + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - '@vitest/spy@2.0.5': - resolution: - { - integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==, - } + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - '@vitest/utils@2.0.5': - resolution: - { - integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==, - } + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - acorn@8.14.0: - resolution: - { - integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==, - } - engines: { node: '>=0.4.0' } + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} hasBin: true - ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true - ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: '>=8' } + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 - ansi-regex@6.0.1: - resolution: - { - integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==, - } - engines: { node: '>=12' } - - ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, - } - engines: { node: '>=4' } - - ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: '>=8' } - - ansi-styles@6.2.1: - resolution: - { - integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==, - } - engines: { node: '>=12' } + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} any-promise@1.3.0: - resolution: - { - integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, - } + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} arg@5.0.2: - resolution: - { - integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==, - } + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } - - aria-query@5.3.2: - resolution: - { - integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, - } - engines: { node: '>= 0.4' } - - array-buffer-byte-length@1.0.1: - resolution: - { - integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==, - } - engines: { node: '>= 0.4' } - - array-includes@3.1.8: - resolution: - { - integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==, - } - engines: { node: '>= 0.4' } - - array-union@2.1.0: - resolution: - { - integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, - } - engines: { node: '>=8' } - - array.prototype.findlast@1.2.5: - resolution: - { - integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==, - } - engines: { node: '>= 0.4' } - - array.prototype.findlastindex@1.2.5: - resolution: - { - integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==, - } - engines: { node: '>= 0.4' } - - array.prototype.flat@1.3.2: - resolution: - { - integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==, - } - engines: { node: '>= 0.4' } - - array.prototype.flatmap@1.3.2: - resolution: - { - integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==, - } - engines: { node: '>= 0.4' } - - array.prototype.tosorted@1.1.4: - resolution: - { - integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==, - } - engines: { node: '>= 0.4' } - - arraybuffer.prototype.slice@1.0.3: - resolution: - { - integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} - ast-types-flow@0.0.8: - resolution: - { - integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, - } - - autoprefixer@10.4.20: - resolution: - { - integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==, - } - engines: { node: ^10 || ^12 || >=14 } + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} + engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 - available-typed-arrays@1.0.7: - resolution: - { - integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, - } - engines: { node: '>= 0.4' } - - axe-core@4.10.2: - resolution: - { - integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==, - } - engines: { node: '>=4' } - - axobject-query@4.1.0: - resolution: - { - integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, - } - engines: { node: '>= 0.4' } - - balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } - - binary-extensions@2.3.0: - resolution: - { - integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, - } - engines: { node: '>=8' } - - brace-expansion@1.1.11: - resolution: - { - integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==, - } - - brace-expansion@2.0.1: - resolution: - { - integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, - } - - braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: '>=8' } - - browserslist@4.23.2: - resolution: - { - integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + baseline-browser-mapping@2.10.31: + resolution: {integrity: sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==} + engines: {node: '>=6.0.0'} hasBin: true - browserslist@4.24.2: - resolution: - { - integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - bun-types@1.1.34: - resolution: - { - integrity: sha512-br5QygTEL/TwB4uQOb96Ky22j4Gq2WxWH/8Oqv20fk5HagwKXo/akB+LiYgSfzexCt6kkcUaVm+bKiPl71xPvw==, - } + bun-types@1.3.14: + resolution: {integrity: sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==} - bun@1.1.29: - resolution: - { - integrity: sha512-SKhpyKNZtgxrVel9ec9xon3LDv8mgpiuFhARgcJo1YIbggY2PBrKHRNiwQ6Qlb+x3ivmRurfuwWgwGexjpgBRg==, - } + bun@1.3.14: + resolution: {integrity: sha512-aB6GVd42x1Y5ie1K16SF+oLGtgSkwX9hgoDdIW88pjvfTccU8F1vfpoOt34QLv0dZ1v3XimtaxPlZUG81Gx9Zg==} cpu: [arm64, x64] - os: [darwin, linux, win32] + os: [darwin, linux, android, freebsd, win32] hasBin: true - bundle-require@5.0.0: - resolution: - { - integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.18' - busboy@1.6.0: - resolution: - { - integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==, - } - engines: { node: '>=10.16.0' } - cac@6.7.14: - resolution: - { - integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, - } - engines: { node: '>=8' } - - call-bind@1.0.7: - resolution: - { - integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==, - } - engines: { node: '>= 0.4' } - - callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} camelcase-css@2.0.1: - resolution: - { - integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} - caniuse-lite@1.0.30001669: - resolution: - { - integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==, - } + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} - chai@5.1.1: - resolution: - { - integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==, - } - engines: { node: '>=12' } + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} - chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, - } - engines: { node: '>=4' } - - chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: '>=10' } - - check-error@2.1.1: - resolution: - { - integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==, - } - engines: { node: '>= 16' } + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} chokidar@3.6.0: - resolution: - { - integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, - } - engines: { node: '>= 8.10.0' } + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} client-only@0.0.1: - resolution: - { - integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, - } + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, - } + clipanion@4.0.0-rc.4: + resolution: {integrity: sha512-CXkMQxU6s9GklO/1f714dkKBMu1lopS1WFF0B8o4AxPykR1hpozxSiUZ5ZUeBjfPgCWqbcNOtZVFhB8Lkfp1+Q==} + peerDependencies: + typanion: '*' - color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: '>=7.0.0' } - - color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, - } - - color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } - - color-string@1.9.1: - resolution: - { - integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==, - } - - color@4.2.3: - resolution: - { - integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==, - } - engines: { node: '>=12.5.0' } + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} commander@2.20.3: - resolution: - { - integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, - } + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} commander@4.1.1: - resolution: - { - integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} - concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - consola@3.2.3: - resolution: - { - integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cross-spawn@7.0.3: - resolution: - { - integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==, - } - engines: { node: '>= 8' } + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} - cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: '>= 8' } + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crossws@0.4.10: + resolution: {integrity: sha512-pz3oubH/dt12KjqsUB0IuXW4nwRDQ583iDsP4555Cpdqx0NoU7pGlWBcayyFI8f/l/idRpgjMEfwuOxSWJYlIA==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true - csstype@3.1.3: - resolution: - { - integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, - } + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - damerau-levenshtein@1.0.8: - resolution: - { - integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==, - } - - data-view-buffer@1.0.1: - resolution: - { - integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==, - } - engines: { node: '>= 0.4' } - - data-view-byte-length@1.0.1: - resolution: - { - integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==, - } - engines: { node: '>= 0.4' } - - data-view-byte-offset@1.0.0: - resolution: - { - integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==, - } - engines: { node: '>= 0.4' } - - debug@3.2.7: - resolution: - { - integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==, - } + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - debug@4.3.6: - resolution: - { - integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==, - } - engines: { node: '>=6.0' } - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.3.7: - resolution: - { - integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, - } - engines: { node: '>=6.0' } - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - dedent@1.5.3: - resolution: - { - integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==, - } + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: babel-plugin-macros: optional: true - deep-eql@5.0.2: - resolution: - { - integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, - } - engines: { node: '>=6' } + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} - deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } - - define-data-property@1.1.4: - resolution: - { - integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, - } - engines: { node: '>= 0.4' } - - define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: '>= 0.4' } + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} detect-libc@1.0.3: - resolution: - { - integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} hasBin: true - detect-libc@2.0.3: - resolution: - { - integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==, - } - engines: { node: '>=8' } + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} didyoumean@1.2.2: - resolution: - { - integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==, - } - - dir-glob@3.0.1: - resolution: - { - integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dlv@1.1.3: - resolution: - { - integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==, - } + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@2.1.0: - resolution: - { - integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==, - } - engines: { node: '>=0.10.0' } + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} - eastasianwidth@0.2.0: - resolution: - { - integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, - } + emnapi@1.11.3: + resolution: {integrity: sha512-+/ZS90YK/rYfVOHtGLHkGffVsnmD/MAKaBHio+Y4XAtg75RLr4cveV/w0jTkUdLM1CcAlaRgG76mpIemWAlk0A==} + peerDependencies: + node-addon-api: '>= 6.1.0' + peerDependenciesMeta: + node-addon-api: + optional: true - electron-to-chromium@1.5.4: - resolution: - { - integrity: sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==, - } - - electron-to-chromium@1.5.45: - resolution: - { - integrity: sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw==, - } - - emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } - - emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } - - enhanced-resolve@5.17.1: - resolution: - { - integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==, - } - engines: { node: '>=10.13.0' } - - es-abstract@1.23.3: - resolution: - { - integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==, - } - engines: { node: '>= 0.4' } - - es-define-property@1.0.0: - resolution: - { - integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==, - } - engines: { node: '>= 0.4' } + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} es-errors@1.3.0: - resolution: - { - integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} - es-iterator-helpers@1.1.0: - resolution: - { - integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==, - } - engines: { node: '>= 0.4' } + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-object-atoms@1.0.0: - resolution: - { - integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==, - } - engines: { node: '>= 0.4' } + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} - es-set-tostringtag@2.0.3: - resolution: - { - integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==, - } - engines: { node: '>= 0.4' } - - es-shim-unscopables@1.0.2: - resolution: - { - integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==, - } - - es-to-primitive@1.2.1: - resolution: - { - integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==, - } - engines: { node: '>= 0.4' } - - esbuild@0.21.5: - resolution: - { - integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==, - } - engines: { node: '>=12' } + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} hasBin: true - esbuild@0.23.0: - resolution: - { - integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==, - } - engines: { node: '>=18' } - hasBin: true - - esbuild@0.23.1: - resolution: - { - integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==, - } - engines: { node: '>=18' } - hasBin: true - - esbuild@0.24.0: - resolution: - { - integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==, - } - engines: { node: '>=18' } - hasBin: true - - escalade@3.1.2: - resolution: - { - integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==, - } - engines: { node: '>=6' } - escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: '>=0.8.0' } - - escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: '>=10' } - - eslint-config-next@15.0.3: - resolution: - { - integrity: sha512-IGP2DdQQrgjcr4mwFPve4DrCqo7CVVez1WoYY47XwKSrYO4hC0Dlb+iJA60i0YfICOzgNADIb8r28BpQ5Zs0wg==, - } - peerDependencies: - eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - - eslint-import-resolver-node@0.3.9: - resolution: - { - integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==, - } - - eslint-import-resolver-typescript@3.6.3: - resolution: - { - integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==, - } - engines: { node: ^14.18.0 || >=16.0.0 } - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-plugin-import-x: '*' - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true - - eslint-module-utils@2.12.0: - resolution: - { - integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==, - } - engines: { node: '>=4' } - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.31.0: - resolution: - { - integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==, - } - engines: { node: '>=4' } - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-jsx-a11y@6.10.1: - resolution: - { - integrity: sha512-zHByM9WTUMnfsDTafGXRiqxp6lFtNoSOWBY6FonVRn3A+BUwN1L/tdBXT40BcBJi0cZjOGTXZ0eD/rTG9fEJ0g==, - } - engines: { node: '>=4.0' } - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - - eslint-plugin-react-hooks@5.0.0: - resolution: - { - integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==, - } - engines: { node: '>=10' } - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react@7.37.2: - resolution: - { - integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==, - } - engines: { node: '>=4' } - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - - eslint-scope@8.2.0: - resolution: - { - integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - - eslint-visitor-keys@4.2.0: - resolution: - { - integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - eslint@9.15.0: - resolution: - { - integrity: sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.3.0: - resolution: - { - integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - esquery@1.6.0: - resolution: - { - integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, - } - engines: { node: '>=0.10' } + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: '>=0.10.0' } + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} - execa@5.1.1: - resolution: - { - integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, - } - engines: { node: '>=10' } + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} - execa@8.0.1: - resolution: - { - integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==, - } - engines: { node: '>=16.17' } + fast-content-type-parse@3.0.0: + resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.1: - resolution: - { - integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==, - } - engines: { node: '>=8.6.0' } + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} - fast-glob@3.3.2: - resolution: - { - integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==, - } - engines: { node: '>=8.6.0' } + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} - fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fastq@1.17.1: - resolution: - { - integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==, - } + fast-stringify@4.0.0: + resolution: {integrity: sha512-lE2DIivBaLysf6hK5WH/VfMgqRbvBVHcpGVVTmA5Zi8oWIjq9YxIt6lYGdUgP1HNSXxTIat7HEIDnrSvXSeKQw==} - file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: '>=16.0.0' } + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} - find-up-simple@1.0.0: - resolution: - { - integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==, - } - engines: { node: '>=18' } + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} - find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: '>=10' } + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} - flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: '>=16' } - - flatted@3.3.1: - resolution: - { - integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==, - } - - for-each@0.3.3: - resolution: - { - integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==, - } - - foreground-child@3.3.0: - resolution: - { - integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==, - } - engines: { node: '>=14' } - - fraction.js@4.3.7: - resolution: - { - integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==, - } + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} fsevents@2.3.2: - resolution: - { - integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: - { - integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==, - } - engines: { node: '>= 0.4' } - - functions-have-names@1.2.3: - resolution: - { - integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, - } - - gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } - - get-func-name@2.0.2: - resolution: - { - integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==, - } - - get-intrinsic@1.2.4: - resolution: - { - integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==, - } - engines: { node: '>= 0.4' } - - get-stream@6.0.1: - resolution: - { - integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, - } - engines: { node: '>=10' } - - get-stream@8.0.1: - resolution: - { - integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==, - } - engines: { node: '>=16' } - - get-symbol-description@1.0.2: - resolution: - { - integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==, - } - engines: { node: '>= 0.4' } - - get-tsconfig@4.8.1: - resolution: - { - integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==, - } + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: - { - integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==, - } - hasBin: true - - globals@11.12.0: - resolution: - { - integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==, - } - engines: { node: '>=4' } - - globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: '>=18' } - - globalthis@1.0.4: - resolution: - { - integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, - } - engines: { node: '>= 0.4' } - - globby@11.1.0: - resolution: - { - integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==, - } - engines: { node: '>=10' } - - globby@14.0.2: - resolution: - { - integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==, - } - engines: { node: '>=18' } - - gopd@1.0.1: - resolution: - { - integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==, - } + globby@16.2.2: + resolution: {integrity: sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==} + engines: {node: '>=20'} graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: - { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, - } - - has-bigints@1.0.2: - resolution: - { - integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==, - } - - has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: '>=4' } + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: - { - integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, - } + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} - has-proto@1.0.3: - resolution: - { - integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==, - } - engines: { node: '>= 0.4' } + http-shutdown@1.2.2: + resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - has-symbols@1.0.3: - resolution: - { - integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==, - } - engines: { node: '>= 0.4' } + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} - has-tostringtag@1.0.2: - resolution: - { - integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, - } - engines: { node: '>= 0.4' } + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} - hasown@2.0.2: - resolution: - { - integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, - } - engines: { node: '>= 0.4' } - - human-signals@2.1.0: - resolution: - { - integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, - } - engines: { node: '>=10.17.0' } - - human-signals@5.0.0: - resolution: - { - integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==, - } - engines: { node: '>=16.17.0' } - - ignore@5.3.1: - resolution: - { - integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==, - } - engines: { node: '>= 4' } - - ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: '>= 4' } - - import-fresh@3.3.0: - resolution: - { - integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==, - } - engines: { node: '>=6' } - - imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: '>=0.8.19' } - - internal-slot@1.0.7: - resolution: - { - integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==, - } - engines: { node: '>= 0.4' } - - is-array-buffer@3.0.4: - resolution: - { - integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==, - } - engines: { node: '>= 0.4' } - - is-arrayish@0.3.2: - resolution: - { - integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==, - } - - is-async-function@2.0.0: - resolution: - { - integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==, - } - engines: { node: '>= 0.4' } - - is-bigint@1.0.4: - resolution: - { - integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==, - } + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} is-binary-path@2.1.0: - resolution: - { - integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} - is-boolean-object@1.1.2: - resolution: - { - integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==, - } - engines: { node: '>= 0.4' } - - is-bun-module@1.2.1: - resolution: - { - integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==, - } - - is-callable@1.2.7: - resolution: - { - integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, - } - engines: { node: '>= 0.4' } - - is-core-module@2.15.0: - resolution: - { - integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==, - } - engines: { node: '>= 0.4' } - - is-core-module@2.15.1: - resolution: - { - integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==, - } - engines: { node: '>= 0.4' } - - is-data-view@1.0.1: - resolution: - { - integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==, - } - engines: { node: '>= 0.4' } - - is-date-object@1.0.5: - resolution: - { - integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==, - } - engines: { node: '>= 0.4' } + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: '>=0.10.0' } - - is-finalizationregistry@1.0.2: - resolution: - { - integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==, - } - - is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: '>=8' } - - is-generator-function@1.0.10: - resolution: - { - integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: '>=0.10.0' } - - is-map@2.0.3: - resolution: - { - integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, - } - engines: { node: '>= 0.4' } - - is-negative-zero@2.0.3: - resolution: - { - integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, - } - engines: { node: '>= 0.4' } - - is-number-object@1.0.7: - resolution: - { - integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} - is-regex@1.1.4: - resolution: - { - integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==, - } - engines: { node: '>= 0.4' } + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} - is-set@2.0.3: - resolution: - { - integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, - } - engines: { node: '>= 0.4' } + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} - is-shared-array-buffer@1.0.3: - resolution: - { - integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==, - } - engines: { node: '>= 0.4' } - - is-stream@2.0.1: - resolution: - { - integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, - } - engines: { node: '>=8' } - - is-stream@3.0.0: - resolution: - { - integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - - is-string@1.0.7: - resolution: - { - integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==, - } - engines: { node: '>= 0.4' } - - is-symbol@1.0.4: - resolution: - { - integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==, - } - engines: { node: '>= 0.4' } - - is-typed-array@1.1.13: - resolution: - { - integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==, - } - engines: { node: '>= 0.4' } - - is-weakmap@2.0.2: - resolution: - { - integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, - } - engines: { node: '>= 0.4' } - - is-weakref@1.0.2: - resolution: - { - integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==, - } - - is-weakset@2.0.3: - resolution: - { - integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==, - } - engines: { node: '>= 0.4' } - - isarray@2.0.5: - resolution: - { - integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, - } - - isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } - - iterator.prototype@1.1.3: - resolution: - { - integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==, - } - engines: { node: '>= 0.4' } - - jackspeak@3.4.3: - resolution: - { - integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, - } - - jiti@1.21.6: - resolution: - { - integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==, - } + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.4.0: - resolution: - { - integrity: sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g==, - } + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true joycon@3.1.1: - resolution: - { - integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} - js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } - - js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true - jsesc@2.5.2: - resolution: - { - integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==, - } - engines: { node: '>=4' } - hasBin: true + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} - json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] - json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } - - json5@1.0.2: - resolution: - { - integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==, - } - hasBin: true - - json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } - hasBin: true - - jsx-ast-utils@3.3.5: - resolution: - { - integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, - } - engines: { node: '>=4.0' } - - keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } - - language-subtag-registry@0.3.23: - resolution: - { - integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==, - } - - language-tags@1.0.9: - resolution: - { - integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==, - } - engines: { node: '>=0.10' } - - levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: '>= 0.8.0' } - - lightningcss-darwin-arm64@1.26.0: - resolution: - { - integrity: sha512-n4TIvHO1NY1ondKFYpL2ZX0bcC2y6yjXMD6JfyizgR8BCFNEeArINDzEaeqlfX9bXz73Bpz/Ow0nu+1qiDrBKg==, - } - engines: { node: '>= 12.0.0' } + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.26.0: - resolution: - { - integrity: sha512-Rf9HuHIDi1R6/zgBkJh25SiJHF+dm9axUZW/0UoYCW1/8HV0gMI0blARhH4z+REmWiU1yYT/KyNF3h7tHyRXUg==, - } - engines: { node: '>= 12.0.0' } + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.26.0: - resolution: - { - integrity: sha512-C/io7POAxp6sZxFSVGezjajMlCKQ8KSwISLLGRq8xLQpQMokYrUoqYEwmIX8mLmF6C/CZPk0gFmRSzd8biWM0g==, - } - engines: { node: '>= 12.0.0' } + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.26.0: - resolution: - { - integrity: sha512-Aag9kqXqkyPSW+dXMgyWk66C984Nay2pY8Nws+67gHlDzV3cWh7TvFlzuaTaVFMVqdDTzN484LSK3u39zFBnzg==, - } - engines: { node: '>= 12.0.0' } + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.26.0: - resolution: - { - integrity: sha512-iJmZM7fUyVjH+POtdiCtExG+67TtPUTer7K/5A8DIfmPfrmeGvzfRyBltGhQz13Wi15K1lf2cPYoRaRh6vcwNA==, - } - engines: { node: '>= 12.0.0' } + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.26.0: - resolution: - { - integrity: sha512-XxoEL++tTkyuvu+wq/QS8bwyTXZv2y5XYCMcWL45b8XwkiS8eEEEej9BkMGSRwxa5J4K+LDeIhLrS23CpQyfig==, - } - engines: { node: '>= 12.0.0' } + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.26.0: - resolution: - { - integrity: sha512-1dkTfZQAYLj8MUSkd6L/+TWTG8V6Kfrzfa0T1fSlXCXQHrt1HC1/UepXHtKHDt/9yFwyoeayivxXAsApVxn6zA==, - } - engines: { node: '>= 12.0.0' } + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.26.0: - resolution: - { - integrity: sha512-yX3Rk9m00JGCUzuUhFEojY+jf/6zHs3XU8S8Vk+FRbnr4St7cjyMXdNjuA2LjiT8e7j8xHRCH8hyZ4H/btRE4A==, - } - engines: { node: '>= 12.0.0' } + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.26.0: - resolution: - { - integrity: sha512-X/597/cFnCogy9VItj/+7Tgu5VLbAtDF7KZDPdSw0MaL6FL940th1y3HiOzFIlziVvAtbo0RB3NAae1Oofr+Tw==, - } - engines: { node: '>= 12.0.0' } + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.26.0: - resolution: - { - integrity: sha512-pYS3EyGP3JRhfqEFYmfFDiZ9/pVNfy8jVIYtrx9TVNusVyDK3gpW1w/rbvroQ4bDJi7grdUtyrYU6V2xkY/bBw==, - } - engines: { node: '>= 12.0.0' } + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.26.0: - resolution: - { - integrity: sha512-a/XZ5hdgifrofQJUArr5AiJjx26SwMam3SJUSMjgebZbESZ96i+6Qsl8tLi0kaUsdMzBWXh9sN1Oe6hp2/dkQw==, - } - engines: { node: '>= 12.0.0' } + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} - lilconfig@2.1.0: - resolution: - { - integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, - } - engines: { node: '>=10' } - - lilconfig@3.1.2: - resolution: - { - integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==, - } - engines: { node: '>=14' } + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + listhen@1.10.1: + resolution: {integrity: sha512-6nt/86SkqUQSLW1ofz8MxC6RhRMqOl3ONISe6qqvJ3xj09aJWQx6DhgSZpugs3PX4PXdOas/WD6A9jx6J2N19A==} + hasBin: true + peerDependencies: + '@parcel/watcher': ^2.5.6 + peerDependenciesMeta: + '@parcel/watcher': + optional: true load-tsconfig@0.2.5: - resolution: - { - integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: '>=10' } + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - lodash.castarray@4.4.0: - resolution: - { - integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==, - } - - lodash.isplainobject@4.0.6: - resolution: - { - integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, - } - - lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } - - lodash.sortby@4.7.0: - resolution: - { - integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==, - } - - loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } - hasBin: true - - loupe@3.1.1: - resolution: - { - integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==, - } - - lru-cache@10.4.3: - resolution: - { - integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, - } - - lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } - - magic-string@0.30.11: - resolution: - { - integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==, - } + magic-string@1.1.0: + resolution: {integrity: sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==} merge-stream@2.0.0: - resolution: - { - integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, - } + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} - micro-memoize@4.1.2: - resolution: - { - integrity: sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==, - } + micro-memoize@5.1.1: + resolution: {integrity: sha512-QDwluos8YeMijiKxZGwaV4f4tzj0soS6+xcsJhJ3+4wdEIHMyKbIKVUziebOgWX3e6yiijdoaHo+9tyhbnaWXA==} - micromatch@4.0.7: - resolution: - { - integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==, - } - engines: { node: '>=8.6' } + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} - mimic-fn@2.1.0: - resolution: - { - integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, - } - engines: { node: '>=6' } - - mimic-fn@4.0.0: - resolution: - { - integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==, - } - engines: { node: '>=12' } + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} mini-svg-data-uri@1.4.4: - resolution: - { - integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==, - } + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } + minimizer-webpack-plugin@5.6.1: + resolution: {integrity: sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@minify-html/node': '*' + '@swc/core': '*' + '@swc/css': '*' + '@swc/html': '*' + clean-css: '*' + cssnano: '*' + csso: '*' + esbuild: '*' + html-minifier-terser: '*' + lightningcss: '*' + postcss: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@minify-html/node': + optional: true + '@swc/core': + optional: true + '@swc/css': + optional: true + '@swc/html': + optional: true + clean-css: + optional: true + cssnano: + optional: true + csso: + optional: true + esbuild: + optional: true + html-minifier-terser: + optional: true + lightningcss: + optional: true + postcss: + optional: true + uglify-js: + optional: true - minimatch@9.0.5: - resolution: - { - integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, - } - engines: { node: '>=16 || 14 >=14.17' } - - minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } - - minipass@7.1.2: - resolution: - { - integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==, - } - engines: { node: '>=16 || 14 >=14.17' } + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} mri@1.2.0: - resolution: - { - integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==, - } - engines: { node: '>=4' } - - ms@2.1.2: - resolution: - { - integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, - } + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} mz@2.7.0: - resolution: - { - integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, - } + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: - { - integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true - next@15.0.3: - resolution: - { - integrity: sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==, - } - engines: { node: ^18.18.0 || ^19.8.0 || >= 20.0.0 } + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + next@16.2.7: + resolution: {integrity: sha512-eMJxgjRzBaj3olkP4cBamHDXL79A8FC6u1GcsO1D1Tsx8bw/LLXUJCaoajVxtnhD3A1IJqIT8IcRJjgBIPJq4w==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 + '@playwright/test': ^1.51.1 babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 - react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': @@ -4654,336 +2937,113 @@ packages: optional: true node-addon-api@7.1.1: - resolution: - { - integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==, - } + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-addon-api@8.2.2: - resolution: - { - integrity: sha512-9emqXAKhVoNrQ792nLI/wpzPpJ/bj/YXxW0CvAau1+RdGBcCRF1Dmz7719zgVsQNrzHl9Tzn3ImZ4qWFarWL0A==, - } - engines: { node: ^18 || ^20 || >= 21 } + node-addon-api@8.7.0: + resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} + engines: {node: ^18 || ^20 || >= 21} - node-gyp-build@4.8.2: - resolution: - { - integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==, - } + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-releases@2.0.18: - resolution: - { - integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==, - } + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: '>=0.10.0' } - - normalize-range@0.1.2: - resolution: - { - integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==, - } - engines: { node: '>=0.10.0' } - - npm-run-path@4.0.1: - resolution: - { - integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, - } - engines: { node: '>=8' } - - npm-run-path@5.3.0: - resolution: - { - integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-hash@3.0.0: - resolution: - { - integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} - object-inspect@1.13.2: - resolution: - { - integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==, - } - engines: { node: '>= 0.4' } + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - object-keys@1.1.1: - resolution: - { - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, - } - engines: { node: '>= 0.4' } - - object.assign@4.1.5: - resolution: - { - integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==, - } - engines: { node: '>= 0.4' } - - object.entries@1.1.8: - resolution: - { - integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==, - } - engines: { node: '>= 0.4' } - - object.fromentries@2.0.8: - resolution: - { - integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==, - } - engines: { node: '>= 0.4' } - - object.groupby@1.0.3: - resolution: - { - integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==, - } - engines: { node: '>= 0.4' } - - object.values@1.2.0: - resolution: - { - integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==, - } - engines: { node: '>= 0.4' } - - onetime@5.1.2: - resolution: - { - integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, - } - engines: { node: '>=6' } - - onetime@6.0.0: - resolution: - { - integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==, - } - engines: { node: '>=12' } - - optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: '>= 0.8.0' } - - p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: '>=10' } - - p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: '>=10' } - - package-json-from-dist@1.0.1: - resolution: - { - integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==, - } + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} package-up@5.0.0: - resolution: - { - integrity: sha512-MQEgDUvXCa3sGvqHg3pzHO8e9gqTCMPVrWUko3vPQGntwegmFo52mZb2abIVTjFnUcW0BcPz0D93jV5Cas1DWA==, - } - engines: { node: '>=18' } - - parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: '>=6' } - - path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: '>=8' } - - path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: '>=8' } - - path-key@4.0.0: - resolution: - { - integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-MQEgDUvXCa3sGvqHg3pzHO8e9gqTCMPVrWUko3vPQGntwegmFo52mZb2abIVTjFnUcW0BcPz0D93jV5Cas1DWA==} + engines: {node: '>=18'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: - { - integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, - } - engines: { node: '>=16 || 14 >=14.18' } - - path-type@4.0.0: - resolution: - { - integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, - } - engines: { node: '>=8' } - - path-type@5.0.0: - resolution: - { - integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==, - } - engines: { node: '>=12' } - - pathe@1.1.2: - resolution: - { - integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==, - } - - pathval@2.0.0: - resolution: - { - integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==, - } - engines: { node: '>= 14.16' } + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: '>=8.6' } + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} pify@2.3.0: - resolution: - { - integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} - pirates@4.0.6: - resolution: - { - integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, - } - engines: { node: '>= 6' } + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} - playwright-core@1.49.0: - resolution: - { - integrity: sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==, - } - engines: { node: '>=18' } + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} hasBin: true - playwright@1.49.0: - resolution: - { - integrity: sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==, - } - engines: { node: '>=18' } + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} hasBin: true - possible-typed-array-names@1.0.0: - resolution: - { - integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==, - } - engines: { node: '>= 0.4' } - postcss-import@15.1.0: - resolution: - { - integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} peerDependencies: postcss: ^8.0.0 - postcss-import@16.1.0: - resolution: - { - integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==, - } - engines: { node: '>=18.0.0' } + postcss-import@16.1.1: + resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} + engines: {node: '>=18.0.0'} peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: - { - integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==, - } - engines: { node: ^12 || ^14 || >= 16 } + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 - postcss-load-config@4.0.2: - resolution: - { - integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==, - } - engines: { node: '>= 14' } - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - postcss-load-config@6.0.1: - resolution: - { - integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: jiti: '>=1.21.0' postcss: '>=8.0.9' @@ -5000,514 +3060,178 @@ packages: optional: true postcss-nested@6.2.0: - resolution: - { - integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==, - } - engines: { node: '>=12.0' } + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 postcss-selector-parser@6.0.10: - resolution: - { - integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} postcss-selector-parser@6.1.2: - resolution: - { - integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} - postcss-selector-parser@7.0.0: - resolution: - { - integrity: sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==, - } - engines: { node: '>=4' } + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} postcss-value-parser@4.2.0: - resolution: - { - integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, - } + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} postcss@8.4.31: - resolution: - { - integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} - postcss@8.4.41: - resolution: - { - integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==, - } - engines: { node: ^10 || ^12 || >=14 } + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} - postcss@8.4.47: - resolution: - { - integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==, - } - engines: { node: ^10 || ^12 || >=14 } + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} + engines: {node: ^10 || ^12 || >=14} - postcss@8.4.49: - resolution: - { - integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==, - } - engines: { node: ^10 || ^12 || >=14 } + prettier-plugin-embed@0.5.1: + resolution: {integrity: sha512-2Ege8gIlLNTvHElUeU5XcFsD7/dbDXkQA6H9TczHSJAGxB58nNjjifk/dlRMS5E29eqQx/z+ToA4ZVMWzjME/A==} - prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: '>= 0.8.0' } - - prettier-plugin-embed@0.4.15: - resolution: - { - integrity: sha512-9pZVIp3bw2jw+Ge+iAMZ4j+sIVC9cPruZ93H2tj5Wa/3YDFDJ/uYyVWdUGfcFUnv28drhW2Bmome9xSGXsPKOw==, - } - - prettier-plugin-organize-imports@4.0.0: - resolution: - { - integrity: sha512-vnKSdgv9aOlqKeEFGhf9SCBsTyzDSyScy1k7E0R1Uo4L0cTcOV7c1XQaT7jfXIOc/p08WLBfN2QUQA9zDSZMxA==, - } + prettier-plugin-organize-imports@4.3.0: + resolution: {integrity: sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw==} peerDependencies: - '@vue/language-plugin-pug': ^2.0.24 prettier: '>=2.0' typescript: '>=2.9' - vue-tsc: ^2.0.24 + vue-tsc: ^2.1.0 || 3 peerDependenciesMeta: - '@vue/language-plugin-pug': - optional: true vue-tsc: optional: true - prettier@3.3.3: - resolution: - { - integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==, - } - engines: { node: '>=14' } + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} hasBin: true - prop-types@15.8.1: - resolution: - { - integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, - } - - punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: '>=6' } - queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-dom@18.3.1: - resolution: - { - integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==, - } + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + react-dom@19.2.6: + resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} peerDependencies: - react: ^18.3.1 + react: ^19.2.6 - react-is@16.13.1: - resolution: - { - integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, - } + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 - react-refresh@0.14.2: - resolution: - { - integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==, - } - engines: { node: '>=0.10.0' } + react@19.2.6: + resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + engines: {node: '>=0.10.0'} - react@18.3.1: - resolution: - { - integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==, - } - engines: { node: '>=0.10.0' } + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} read-cache@1.0.0: - resolution: - { - integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==, - } + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} readdirp@3.6.0: - resolution: - { - integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, - } - engines: { node: '>=8.10.0' } + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} - reflect.getprototypeof@1.0.6: - resolution: - { - integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==, - } - engines: { node: '>= 0.4' } + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} - regexp.prototype.flags@1.5.2: - resolution: - { - integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==, - } - engines: { node: '>= 0.4' } - - resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: '>=4' } + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } - - resolve@1.22.8: - resolution: - { - integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==, - } + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.5: - resolution: - { - integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==, - } + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rolldown@1.2.1: + resolution: {integrity: sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - reusify@1.0.4: - resolution: - { - integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, - } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } - - rollup@4.20.0: - resolution: - { - integrity: sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==, - } - engines: { node: '>=18.0.0', npm: '>=8.0.0' } - hasBin: true - - rollup@4.27.4: - resolution: - { - integrity: sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==, - } - engines: { node: '>=18.0.0', npm: '>=8.0.0' } + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.2: - resolution: - { - integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==, - } - engines: { node: '>=0.4' } + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - safe-regex-test@1.0.3: - resolution: - { - integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==, - } - engines: { node: '>= 0.4' } + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - scheduler@0.23.2: - resolution: - { - integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==, - } + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} - semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} hasBin: true - semver@7.6.3: - resolution: - { - integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==, - } - engines: { node: '>=10' } - hasBin: true - - set-function-length@1.2.2: - resolution: - { - integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, - } - engines: { node: '>= 0.4' } - - set-function-name@2.0.2: - resolution: - { - integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==, - } - engines: { node: '>= 0.4' } - - sharp@0.33.5: - resolution: - { - integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } - - shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: '>=8' } - - shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: '>=8' } - - side-channel@1.0.6: - resolution: - { - integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==, - } - engines: { node: '>= 0.4' } + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } - - signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, - } + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: '>=14' } - - simple-swizzle@0.2.2: - resolution: - { - integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==, - } - - slash@3.0.0: - resolution: - { - integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} slash@5.1.0: - resolution: - { - integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==, - } - engines: { node: '>=14.16' } - - source-map-js@1.2.0: - resolution: - { - integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-support@0.5.21: - resolution: - { - integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, - } + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} - source-map@0.8.0-beta.0: - resolution: - { - integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==, - } - engines: { node: '>= 8' } + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.7.0: - resolution: - { - integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==, - } + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - streamsearch@1.1.0: - resolution: - { - integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==, - } - engines: { node: '>=10.0.0' } - - string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: '>=8' } - - string-width@5.1.2: - resolution: - { - integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, - } - engines: { node: '>=12' } - - string.prototype.includes@2.0.1: - resolution: - { - integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==, - } - engines: { node: '>= 0.4' } - - string.prototype.matchall@4.0.11: - resolution: - { - integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==, - } - engines: { node: '>= 0.4' } - - string.prototype.repeat@1.0.0: - resolution: - { - integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==, - } - - string.prototype.trim@1.2.9: - resolution: - { - integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==, - } - engines: { node: '>= 0.4' } - - string.prototype.trimend@1.0.8: - resolution: - { - integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==, - } - - string.prototype.trimstart@1.0.8: - resolution: - { - integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, - } - engines: { node: '>= 0.4' } - - strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: '>=8' } - - strip-ansi@7.1.0: - resolution: - { - integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==, - } - engines: { node: '>=12' } - - strip-bom@3.0.0: - resolution: - { - integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, - } - engines: { node: '>=4' } - - strip-final-newline@2.0.0: - resolution: - { - integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, - } - engines: { node: '>=6' } - - strip-final-newline@3.0.0: - resolution: - { - integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==, - } - engines: { node: '>=12' } - - strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: '>=8' } + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} styled-jsx@5.1.6: - resolution: - { - integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, - } - engines: { node: '>= 12.0.0' } + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' babel-plugin-macros: '*' @@ -5518,136 +3242,83 @@ packages: babel-plugin-macros: optional: true - sucrase@3.35.0: - resolution: - { - integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==, - } - engines: { node: '>=16 || 14 >=14.17' } + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: '>=4' } - - supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: '>=8' } + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} - tailwindcss@3.4.14: - resolution: - { - integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==, - } - engines: { node: '>=14.0.0' } + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} + engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.1: - resolution: - { - integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==, - } - engines: { node: '>=6' } + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} - terser@5.31.6: - resolution: - { - integrity: sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==, - } - engines: { node: '>=10' } + terser@5.48.0: + resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + engines: {node: '>=10'} hasBin: true thenify-all@1.6.0: - resolution: - { - integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} thenify@3.3.1: - resolution: - { - integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, - } + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tiny-jsonc@1.0.1: - resolution: - { - integrity: sha512-ik6BCxzva9DoiEfDX/li0L2cWKPPENYvixUprFdl3YPi4bZZUhDnNI9YUkacrv+uIG90dnxR5mNqaoD6UhD6Bw==, - } + tiny-jsonc@1.0.2: + resolution: {integrity: sha512-f5QDAfLq6zIVSyCZQZhhyl0QS6MvAyTxgz4X4x3+EoCktNWEYJ6PeoEA97fyb98njpBNNi88ybpD7m+BDFXaCw==} tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinypool@1.0.0: - resolution: - { - integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + tinyclip@0.1.15: + resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} + engines: {node: ^16.14.0 || >= 17.3.0} - tinyrainbow@1.2.0: - resolution: - { - integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==, - } - engines: { node: '>=14.0.0' } + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyspy@3.0.0: - resolution: - { - integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==, - } - engines: { node: '>=14.0.0' } + tinyexec@1.1.2: + resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==} + engines: {node: '>=18'} - to-fast-properties@2.0.0: - resolution: - { - integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, - } - engines: { node: '>=4' } + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: '>=8.0' } - - tr46@1.0.1: - resolution: - { - integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==, - } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} tree-kill@1.2.2: - resolution: - { - integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, - } + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true tree-sitter-javascript@0.23.1: - resolution: - { - integrity: sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA==, - } + resolution: {integrity: sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA==} peerDependencies: tree-sitter: ^0.21.1 peerDependenciesMeta: @@ -5655,55 +3326,25 @@ packages: optional: true tree-sitter-typescript@0.23.2: - resolution: - { - integrity: sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==, - } + resolution: {integrity: sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==} peerDependencies: tree-sitter: ^0.21.0 peerDependenciesMeta: tree-sitter: optional: true - tree-sitter@0.22.1: - resolution: - { - integrity: sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw==, - } - - ts-api-utils@1.3.0: - resolution: - { - integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==, - } - engines: { node: '>=16' } - peerDependencies: - typescript: '>=4.2.0' + tree-sitter@0.25.1: + resolution: {integrity: sha512-mrcEdkYtHfrK1A6fs3O6FxkBo0Qig5XUXqHhxUOQu0bmPo00QF4XaSx4edpazdHwxnSCjlGKGgIqWdaN4dvTLA==} ts-interface-checker@0.1.13: - resolution: - { - integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, - } + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfig-paths@3.15.0: - resolution: - { - integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==, - } + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tslib@2.8.0: - resolution: - { - integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==, - } - - tsup@8.2.4: - resolution: - { - integrity: sha512-akpCPePnBnC/CXgRrcy72ZSntgIEUa1jN0oJbbvpALWKNOz1B7aM+UVDWGRGIO/T/PZugAESWDJUAb5FD48o8Q==, - } - engines: { node: '>=18' } + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} hasBin: true peerDependencies: '@microsoft/api-extractor': ^7.36.0 @@ -5720,246 +3361,90 @@ packages: typescript: optional: true - tsx@4.19.1: - resolution: - { - integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==, - } - engines: { node: '>=18.0.0' } + turbo@2.10.8: + resolution: {integrity: sha512-9+8YX5QOkGXzZxcIykTHgaooRHGMWO+jfdyRK0o+rN0U7hBIig2MrJ8r/aNzIPDPhdA73SGb0O+tIztaModTMg==} hasBin: true - turbo-darwin-64@2.2.3: - resolution: - { - integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==, - } - cpu: [x64] - os: [darwin] + typanion@3.14.0: + resolution: {integrity: sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==} - turbo-darwin-arm64@2.2.3: - resolution: - { - integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==, - } - cpu: [arm64] - os: [darwin] + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} - turbo-linux-64@2.2.3: - resolution: - { - integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==, - } - cpu: [x64] - os: [linux] - - turbo-linux-arm64@2.2.3: - resolution: - { - integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==, - } - cpu: [arm64] - os: [linux] - - turbo-windows-64@2.2.3: - resolution: - { - integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==, - } - cpu: [x64] - os: [win32] - - turbo-windows-arm64@2.2.3: - resolution: - { - integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==, - } - cpu: [arm64] - os: [win32] - - turbo@2.2.3: - resolution: - { - integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==, - } + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true - type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: '>= 0.8.0' } - - type-fest@4.24.0: - resolution: - { - integrity: sha512-spAaHzc6qre0TlZQQ2aA/nGMe+2Z/wyGk5Z+Ru2VUfdNwT6kWO6TjevOlpebsATEG1EIQ2sOiDszud3lO5mt/Q==, - } - engines: { node: '>=16' } - - typed-array-buffer@1.0.2: - resolution: - { - integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==, - } - engines: { node: '>= 0.4' } - - typed-array-byte-length@1.0.1: - resolution: - { - integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==, - } - engines: { node: '>= 0.4' } - - typed-array-byte-offset@1.0.2: - resolution: - { - integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==, - } - engines: { node: '>= 0.4' } - - typed-array-length@1.0.6: - resolution: - { - integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==, - } - engines: { node: '>= 0.4' } - - typescript@5.5.4: - resolution: - { - integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==, - } - engines: { node: '>=14.17' } + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} hasBin: true - typescript@5.6.3: - resolution: - { - integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==, - } - engines: { node: '>=14.17' } + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + + untun@0.2.2: + resolution: {integrity: sha512-+NnOJcSiEtYsVgJmXUzQbJeRAFXJC4yPJYuh6kF9B0Rm6zunXcs/3GZOTllyocSbUDIxD6Bj7e/4ATw7sph1Sw==} hasBin: true - unbox-primitive@1.0.2: - resolution: - { - integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==, - } - - undici-types@5.26.5: - resolution: - { - integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==, - } - - unicorn-magic@0.1.0: - resolution: - { - integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==, - } - engines: { node: '>=18' } - - update-browserslist-db@1.1.0: - resolution: - { - integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==, - } + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' - update-browserslist-db@1.1.1: - resolution: - { - integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==, - } - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + uqr@0.1.3: + resolution: {integrity: sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==} util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite-node@2.0.5: - resolution: - { - integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==, - } - engines: { node: ^18.0.0 || >=20.0.0 } - hasBin: true - - vite@5.4.0: - resolution: - { - integrity: sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + vite@8.2.0: + resolution: {integrity: sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vite@6.0.0: - resolution: - { - integrity: sha512-Q2+5yQV79EdnpbNxjD3/QHVMCBaQ3Kpd4/uL51UGuh38bIIM+s4o3FqyCzRvTRwFb+cWIUeZvaWwS9y2LD2qeQ==, - } - engines: { node: ^18.0.0 || ^20.0.0 || >=22.0.0 } - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -5975,26 +3460,39 @@ packages: yaml: optional: true - vitest@2.0.5: - resolution: - { - integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true + '@opentelemetry/api': + optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true @@ -6003,716 +3501,704 @@ packages: jsdom: optional: true - webidl-conversions@4.0.2: - resolution: - { - integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==, - } + watchpack@2.5.2: + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + engines: {node: '>=10.13.0'} - whatwg-url@7.1.0: - resolution: - { - integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==, - } + webpack-sources@3.5.1: + resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} + engines: {node: '>=10.13.0'} - which-boxed-primitive@1.0.2: - resolution: - { - integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==, - } - - which-builtin-type@1.1.4: - resolution: - { - integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==, - } - engines: { node: '>= 0.4' } - - which-collection@1.0.2: - resolution: - { - integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, - } - engines: { node: '>= 0.4' } - - which-typed-array@1.1.15: - resolution: - { - integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==, - } - engines: { node: '>= 0.4' } - - which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: '>= 8' } + webpack@5.109.2: + resolution: {integrity: sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==} + engines: {node: '>=10.13.0'} hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} hasBin: true - word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: '>=0.10.0' } - - wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: '>=10' } - - wrap-ansi@8.1.0: - resolution: - { - integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, - } - engines: { node: '>=12' } - - yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } - - yaml@2.6.0: - resolution: - { - integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==, - } - engines: { node: '>= 14' } + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} hasBin: true - yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: '>=10' } - snapshots: + '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': + '@emnapi/core@1.10.0': dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true - '@babel/code-frame@7.24.7': + '@emnapi/core@1.11.3': dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.1.1 + '@emnapi/wasi-threads': 1.2.3 + tslib: 2.8.1 - '@babel/compat-data@7.25.2': {} - - '@babel/core@7.25.2': + '@emnapi/core@2.0.0-alpha.3': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.0 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helpers': 7.25.0 - '@babel/parser': 7.25.3 - '@babel/template': 7.25.0 - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 - convert-source-map: 2.0.0 - debug: 4.3.7 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@emnapi/wasi-threads': 2.0.1 + tslib: 2.8.1 + optional: true - '@babel/generator@7.25.0': + '@emnapi/runtime@1.10.0': dependencies: - '@babel/types': 7.25.2 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 + tslib: 2.8.1 + optional: true - '@babel/helper-compilation-targets@7.25.2': + '@emnapi/runtime@1.11.3': dependencies: - '@babel/compat-data': 7.25.2 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.2 - lru-cache: 5.1.1 - semver: 6.3.1 + tslib: 2.8.1 - '@babel/helper-module-imports@7.24.7': + '@emnapi/runtime@2.0.0-alpha.3': dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 - transitivePeerDependencies: - - supports-color + tslib: 2.8.1 + optional: true - '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + '@emnapi/wasi-threads@1.2.1': dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.3 - transitivePeerDependencies: - - supports-color + tslib: 2.8.1 + optional: true - '@babel/helper-plugin-utils@7.24.8': {} - - '@babel/helper-simple-access@7.24.7': + '@emnapi/wasi-threads@1.2.3': dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 - transitivePeerDependencies: - - supports-color + tslib: 2.8.1 - '@babel/helper-string-parser@7.24.8': {} - - '@babel/helper-validator-identifier@7.24.7': {} - - '@babel/helper-validator-option@7.24.8': {} - - '@babel/helpers@7.25.0': + '@emnapi/wasi-threads@2.0.1': dependencies: - '@babel/template': 7.25.0 - '@babel/types': 7.25.2 - - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/parser@7.25.3': - dependencies: - '@babel/types': 7.25.2 - - '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)': - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - - '@babel/template@7.25.0': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 - - '@babel/traverse@7.25.3': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.0 - '@babel/parser': 7.25.3 - '@babel/template': 7.25.0 - '@babel/types': 7.25.2 - debug: 4.3.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.25.2': - dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 - - '@emnapi/runtime@1.3.1': - dependencies: - tslib: 2.8.0 - optional: true - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.23.0': - optional: true - - '@esbuild/aix-ppc64@0.23.1': - optional: true - - '@esbuild/aix-ppc64@0.24.0': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.23.0': - optional: true - - '@esbuild/android-arm64@0.23.1': - optional: true - - '@esbuild/android-arm64@0.24.0': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.23.0': - optional: true - - '@esbuild/android-arm@0.23.1': - optional: true - - '@esbuild/android-arm@0.24.0': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.23.0': - optional: true - - '@esbuild/android-x64@0.23.1': - optional: true - - '@esbuild/android-x64@0.24.0': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.23.0': - optional: true - - '@esbuild/darwin-arm64@0.23.1': - optional: true - - '@esbuild/darwin-arm64@0.24.0': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.23.0': - optional: true - - '@esbuild/darwin-x64@0.23.1': - optional: true - - '@esbuild/darwin-x64@0.24.0': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.23.0': - optional: true - - '@esbuild/freebsd-arm64@0.23.1': - optional: true - - '@esbuild/freebsd-arm64@0.24.0': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.23.0': - optional: true - - '@esbuild/freebsd-x64@0.23.1': - optional: true - - '@esbuild/freebsd-x64@0.24.0': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.23.0': - optional: true - - '@esbuild/linux-arm64@0.23.1': - optional: true - - '@esbuild/linux-arm64@0.24.0': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-arm@0.23.0': - optional: true - - '@esbuild/linux-arm@0.23.1': - optional: true - - '@esbuild/linux-arm@0.24.0': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.23.0': - optional: true - - '@esbuild/linux-ia32@0.23.1': - optional: true - - '@esbuild/linux-ia32@0.24.0': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.23.0': - optional: true - - '@esbuild/linux-loong64@0.23.1': - optional: true - - '@esbuild/linux-loong64@0.24.0': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.23.0': - optional: true - - '@esbuild/linux-mips64el@0.23.1': - optional: true - - '@esbuild/linux-mips64el@0.24.0': - optional: true - - '@esbuild/linux-ppc64@0.21.5': + tslib: 2.8.1 optional: true - '@esbuild/linux-ppc64@0.23.0': + '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.23.1': + '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.24.0': + '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.23.0': + '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.23.1': + '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.24.0': + '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-s390x@0.23.0': + '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/linux-s390x@0.23.1': + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-s390x@0.24.0': + '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/linux-x64@0.23.0': + '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/linux-x64@0.23.1': + '@esbuild/linux-ppc64@0.27.7': optional: true - '@esbuild/linux-x64@0.24.0': + '@esbuild/linux-riscv64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/linux-s390x@0.27.7': optional: true - '@esbuild/netbsd-x64@0.23.0': + '@esbuild/linux-x64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.23.1': + '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.24.0': + '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.23.0': + '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.23.1': + '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.24.0': + '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.23.0': + '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.23.1': + '@esbuild/win32-ia32@0.27.7': optional: true - '@esbuild/openbsd-x64@0.24.0': + '@esbuild/win32-x64@0.27.7': optional: true - '@esbuild/sunos-x64@0.21.5': + '@img/colour@1.1.0': optional: true - '@esbuild/sunos-x64@0.23.0': - optional: true - - '@esbuild/sunos-x64@0.23.1': - optional: true - - '@esbuild/sunos-x64@0.24.0': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.23.0': - optional: true - - '@esbuild/win32-arm64@0.23.1': - optional: true - - '@esbuild/win32-arm64@0.24.0': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.23.0': - optional: true - - '@esbuild/win32-ia32@0.23.1': - optional: true - - '@esbuild/win32-ia32@0.24.0': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.23.0': - optional: true - - '@esbuild/win32-x64@0.23.1': - optional: true - - '@esbuild/win32-x64@0.24.0': - optional: true - - '@eslint-community/eslint-utils@4.4.0(eslint@9.15.0(jiti@2.4.0))': - dependencies: - eslint: 9.15.0(jiti@2.4.0) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.11.1': {} - - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.19.0': - dependencies: - '@eslint/object-schema': 2.1.4 - debug: 4.3.7 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/core@0.9.0': {} - - '@eslint/eslintrc@3.2.0': - dependencies: - ajv: 6.12.6 - debug: 4.3.7 - espree: 10.3.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.15.0': {} - - '@eslint/object-schema@2.1.4': {} - - '@eslint/plugin-kit@0.2.3': - dependencies: - levn: 0.4.1 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.6': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.1': {} - - '@img/sharp-darwin-arm64@0.33.5': + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.33.5': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.0.4': + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.0.4': + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.0.4': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.0.5': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.0.4': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.0.4': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.0.4': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-linux-arm64@0.33.5': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm@0.33.5': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.33.5': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-x64@0.33.5': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.33.5': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.33.5': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-wasm32@0.33.5': + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.3.1 + '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-win32-ia32@0.33.5': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-x64@0.33.5': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@isaacs/cliui@8.0.2': + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@inquirer/ansi@2.0.7': {} + + '@inquirer/checkbox@5.2.1(@types/node@25.9.1)': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 - '@jridgewell/gen-mapping@0.3.5': + '@inquirer/confirm@6.1.1(@types/node@25.9.1)': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/core@11.2.1(@types/node@25.9.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/editor@5.2.2(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/external-editor': 3.0.3(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/expand@5.1.1(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/external-editor@3.0.3(@types/node@25.9.1)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.2 + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/figures@2.0.7': {} + + '@inquirer/input@5.1.2(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/number@4.1.1(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/password@5.1.1(@types/node@25.9.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/prompts@8.5.2(@types/node@25.9.1)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@25.9.1) + '@inquirer/confirm': 6.1.1(@types/node@25.9.1) + '@inquirer/editor': 5.2.2(@types/node@25.9.1) + '@inquirer/expand': 5.1.1(@types/node@25.9.1) + '@inquirer/input': 5.1.2(@types/node@25.9.1) + '@inquirer/number': 4.1.1(@types/node@25.9.1) + '@inquirer/password': 5.1.1(@types/node@25.9.1) + '@inquirer/rawlist': 5.3.1(@types/node@25.9.1) + '@inquirer/search': 4.2.1(@types/node@25.9.1) + '@inquirer/select': 5.2.1(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/rawlist@5.3.1(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/search@4.2.1(@types/node@25.9.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/select@5.2.1(@types/node@25.9.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@25.9.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@25.9.1) + optionalDependencies: + '@types/node': 25.9.1 + + '@inquirer/type@4.0.7(@types/node@25.9.1)': + optionalDependencies: + '@types/node': 25.9.1 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/source-map@0.3.6': + '@jridgewell/source-map@0.3.11': dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - optional: true + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 - '@napi-rs/cli@2.18.4': {} - - '@next/env@15.0.3': {} - - '@next/eslint-plugin-next@15.0.3': + '@napi-rs/cli@3.7.4(patch_hash=9912bf0a9c2cef8329d11c41fbce4d33ce2bdcf660a630b258c962c0a3c44bed)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@25.9.1)(node-addon-api@8.7.0)': dependencies: - fast-glob: 3.3.1 + '@inquirer/prompts': 8.5.2(@types/node@25.9.1) + '@napi-rs/cross-toolchain': 1.0.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/wasm-tools': 1.0.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@octokit/rest': 22.0.1 + clipanion: 4.0.0-rc.4(typanion@3.14.0) + colorette: 2.0.20 + emnapi: 1.11.3(node-addon-api@8.7.0) + es-toolkit: 1.50.0 + js-yaml: 4.3.1 + obug: 2.1.4 + semver: 7.8.5 + typanion: 3.14.0 + optionalDependencies: + '@emnapi/runtime': 1.11.3 + transitivePeerDependencies: + - '@emnapi/core' + - '@napi-rs/cross-toolchain-arm64-target-aarch64' + - '@napi-rs/cross-toolchain-arm64-target-armv7' + - '@napi-rs/cross-toolchain-arm64-target-ppc64le' + - '@napi-rs/cross-toolchain-arm64-target-s390x' + - '@napi-rs/cross-toolchain-arm64-target-x86_64' + - '@napi-rs/cross-toolchain-x64-target-aarch64' + - '@napi-rs/cross-toolchain-x64-target-armv7' + - '@napi-rs/cross-toolchain-x64-target-ppc64le' + - '@napi-rs/cross-toolchain-x64-target-s390x' + - '@napi-rs/cross-toolchain-x64-target-x86_64' + - '@types/node' + - node-addon-api + - supports-color - '@next/swc-darwin-arm64@15.0.3': + '@napi-rs/cross-toolchain@1.0.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@napi-rs/lzma': 1.4.5(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/tar': 1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + debug: 4.4.3 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + - supports-color + + '@napi-rs/lzma-android-arm-eabi@1.4.5': optional: true - '@next/swc-darwin-x64@15.0.3': + '@napi-rs/lzma-android-arm64@1.4.5': optional: true - '@next/swc-linux-arm64-gnu@15.0.3': + '@napi-rs/lzma-darwin-arm64@1.4.5': optional: true - '@next/swc-linux-arm64-musl@15.0.3': + '@napi-rs/lzma-darwin-x64@1.4.5': optional: true - '@next/swc-linux-x64-gnu@15.0.3': + '@napi-rs/lzma-freebsd-x64@1.4.5': optional: true - '@next/swc-linux-x64-musl@15.0.3': + '@napi-rs/lzma-linux-arm-gnueabihf@1.4.5': optional: true - '@next/swc-win32-arm64-msvc@15.0.3': + '@napi-rs/lzma-linux-arm64-gnu@1.4.5': optional: true - '@next/swc-win32-x64-msvc@15.0.3': + '@napi-rs/lzma-linux-arm64-musl@1.4.5': + optional: true + + '@napi-rs/lzma-linux-ppc64-gnu@1.4.5': + optional: true + + '@napi-rs/lzma-linux-riscv64-gnu@1.4.5': + optional: true + + '@napi-rs/lzma-linux-s390x-gnu@1.4.5': + optional: true + + '@napi-rs/lzma-linux-x64-gnu@1.4.5': + optional: true + + '@napi-rs/lzma-linux-x64-musl@1.4.5': + optional: true + + '@napi-rs/lzma-wasm32-wasi@1.4.5(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@napi-rs/lzma-win32-arm64-msvc@1.4.5': + optional: true + + '@napi-rs/lzma-win32-ia32-msvc@1.4.5': + optional: true + + '@napi-rs/lzma-win32-x64-msvc@1.4.5': + optional: true + + '@napi-rs/lzma@1.4.5(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + optionalDependencies: + '@napi-rs/lzma-android-arm-eabi': 1.4.5 + '@napi-rs/lzma-android-arm64': 1.4.5 + '@napi-rs/lzma-darwin-arm64': 1.4.5 + '@napi-rs/lzma-darwin-x64': 1.4.5 + '@napi-rs/lzma-freebsd-x64': 1.4.5 + '@napi-rs/lzma-linux-arm-gnueabihf': 1.4.5 + '@napi-rs/lzma-linux-arm64-gnu': 1.4.5 + '@napi-rs/lzma-linux-arm64-musl': 1.4.5 + '@napi-rs/lzma-linux-ppc64-gnu': 1.4.5 + '@napi-rs/lzma-linux-riscv64-gnu': 1.4.5 + '@napi-rs/lzma-linux-s390x-gnu': 1.4.5 + '@napi-rs/lzma-linux-x64-gnu': 1.4.5 + '@napi-rs/lzma-linux-x64-musl': 1.4.5 + '@napi-rs/lzma-wasm32-wasi': 1.4.5(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/lzma-win32-arm64-msvc': 1.4.5 + '@napi-rs/lzma-win32-ia32-msvc': 1.4.5 + '@napi-rs/lzma-win32-x64-msvc': 1.4.5 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@napi-rs/tar-android-arm-eabi@1.1.0': + optional: true + + '@napi-rs/tar-android-arm64@1.1.0': + optional: true + + '@napi-rs/tar-darwin-arm64@1.1.0': + optional: true + + '@napi-rs/tar-darwin-x64@1.1.0': + optional: true + + '@napi-rs/tar-freebsd-x64@1.1.0': + optional: true + + '@napi-rs/tar-linux-arm-gnueabihf@1.1.0': + optional: true + + '@napi-rs/tar-linux-arm64-gnu@1.1.0': + optional: true + + '@napi-rs/tar-linux-arm64-musl@1.1.0': + optional: true + + '@napi-rs/tar-linux-ppc64-gnu@1.1.0': + optional: true + + '@napi-rs/tar-linux-s390x-gnu@1.1.0': + optional: true + + '@napi-rs/tar-linux-x64-gnu@1.1.0': + optional: true + + '@napi-rs/tar-linux-x64-musl@1.1.0': + optional: true + + '@napi-rs/tar-wasm32-wasi@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@napi-rs/tar-win32-arm64-msvc@1.1.0': + optional: true + + '@napi-rs/tar-win32-ia32-msvc@1.1.0': + optional: true + + '@napi-rs/tar-win32-x64-msvc@1.1.0': + optional: true + + '@napi-rs/tar@1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + optionalDependencies: + '@napi-rs/tar-android-arm-eabi': 1.1.0 + '@napi-rs/tar-android-arm64': 1.1.0 + '@napi-rs/tar-darwin-arm64': 1.1.0 + '@napi-rs/tar-darwin-x64': 1.1.0 + '@napi-rs/tar-freebsd-x64': 1.1.0 + '@napi-rs/tar-linux-arm-gnueabihf': 1.1.0 + '@napi-rs/tar-linux-arm64-gnu': 1.1.0 + '@napi-rs/tar-linux-arm64-musl': 1.1.0 + '@napi-rs/tar-linux-ppc64-gnu': 1.1.0 + '@napi-rs/tar-linux-s390x-gnu': 1.1.0 + '@napi-rs/tar-linux-x64-gnu': 1.1.0 + '@napi-rs/tar-linux-x64-musl': 1.1.0 + '@napi-rs/tar-wasm32-wasi': 1.1.0(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/tar-win32-arm64-msvc': 1.1.0 + '@napi-rs/tar-win32-ia32-msvc': 1.1.0 + '@napi-rs/tar-win32-x64-msvc': 1.1.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.3 + + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)': + dependencies: + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-tools-android-arm-eabi@1.0.1': + optional: true + + '@napi-rs/wasm-tools-android-arm64@1.0.1': + optional: true + + '@napi-rs/wasm-tools-darwin-arm64@1.0.1': + optional: true + + '@napi-rs/wasm-tools-darwin-x64@1.0.1': + optional: true + + '@napi-rs/wasm-tools-freebsd-x64@1.0.1': + optional: true + + '@napi-rs/wasm-tools-linux-arm64-gnu@1.0.1': + optional: true + + '@napi-rs/wasm-tools-linux-arm64-musl@1.0.1': + optional: true + + '@napi-rs/wasm-tools-linux-x64-gnu@1.0.1': + optional: true + + '@napi-rs/wasm-tools-linux-x64-musl@1.0.1': + optional: true + + '@napi-rs/wasm-tools-wasm32-wasi@1.0.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@napi-rs/wasm-tools-win32-arm64-msvc@1.0.1': + optional: true + + '@napi-rs/wasm-tools-win32-ia32-msvc@1.0.1': + optional: true + + '@napi-rs/wasm-tools-win32-x64-msvc@1.0.1': + optional: true + + '@napi-rs/wasm-tools@1.0.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + optionalDependencies: + '@napi-rs/wasm-tools-android-arm-eabi': 1.0.1 + '@napi-rs/wasm-tools-android-arm64': 1.0.1 + '@napi-rs/wasm-tools-darwin-arm64': 1.0.1 + '@napi-rs/wasm-tools-darwin-x64': 1.0.1 + '@napi-rs/wasm-tools-freebsd-x64': 1.0.1 + '@napi-rs/wasm-tools-linux-arm64-gnu': 1.0.1 + '@napi-rs/wasm-tools-linux-arm64-musl': 1.0.1 + '@napi-rs/wasm-tools-linux-x64-gnu': 1.0.1 + '@napi-rs/wasm-tools-linux-x64-musl': 1.0.1 + '@napi-rs/wasm-tools-wasm32-wasi': 1.0.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/wasm-tools-win32-arm64-msvc': 1.0.1 + '@napi-rs/wasm-tools-win32-ia32-msvc': 1.0.1 + '@napi-rs/wasm-tools-win32-x64-msvc': 1.0.1 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@next/env@16.2.7': {} + + '@next/swc-darwin-arm64@16.2.7': + optional: true + + '@next/swc-darwin-x64@16.2.7': + optional: true + + '@next/swc-linux-arm64-gnu@16.2.7': + optional: true + + '@next/swc-linux-arm64-musl@16.2.7': + optional: true + + '@next/swc-linux-x64-gnu@16.2.7': + optional: true + + '@next/swc-linux-x64-musl@16.2.7': + optional: true + + '@next/swc-win32-arm64-msvc@16.2.7': + optional: true + + '@next/swc-win32-x64-msvc@16.2.7': optional: true '@nodelib/fs.scandir@2.1.5': @@ -6725,705 +4211,665 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + fastq: 1.20.1 - '@nolyfill/is-core-module@1.0.39': {} + '@octokit/auth-token@6.0.0': {} - '@oven/bun-darwin-aarch64@1.1.29': - optional: true - - '@oven/bun-darwin-x64-baseline@1.1.29': - optional: true - - '@oven/bun-darwin-x64@1.1.29': - optional: true - - '@oven/bun-linux-aarch64@1.1.29': - optional: true - - '@oven/bun-linux-x64-baseline@1.1.29': - optional: true - - '@oven/bun-linux-x64@1.1.29': - optional: true - - '@oven/bun-windows-x64-baseline@1.1.29': - optional: true - - '@oven/bun-windows-x64@1.1.29': - optional: true - - '@parcel/watcher-android-arm64@2.5.0': - optional: true - - '@parcel/watcher-darwin-arm64@2.5.0': {} - - '@parcel/watcher-darwin-x64@2.5.0': {} - - '@parcel/watcher-freebsd-x64@2.5.0': - optional: true - - '@parcel/watcher-linux-arm-glibc@2.5.0': - optional: true - - '@parcel/watcher-linux-arm-musl@2.5.0': - optional: true - - '@parcel/watcher-linux-arm64-glibc@2.5.0': {} - - '@parcel/watcher-linux-arm64-musl@2.5.0': {} - - '@parcel/watcher-linux-x64-glibc@2.5.0': {} - - '@parcel/watcher-linux-x64-musl@2.5.0': {} - - '@parcel/watcher-win32-arm64@2.5.0': - optional: true - - '@parcel/watcher-win32-ia32@2.5.0': - optional: true - - '@parcel/watcher-win32-x64@2.5.0': {} - - '@parcel/watcher@2.5.0(patch_hash=pyfayeoxwesjxsefsxp3jpev4q)': + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.9 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.9 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/openapi-types@27.0.0': {} + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.9': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + content-type: 2.0.0 + fast-content-type-parse: 3.0.0 + json-with-bigint: 3.5.8 + universal-user-agent: 7.0.3 + + '@octokit/rest@22.0.1': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@oven/bun-darwin-aarch64@1.3.14': + optional: true + + '@oven/bun-darwin-x64-baseline@1.3.14': + optional: true + + '@oven/bun-darwin-x64@1.3.14': + optional: true + + '@oven/bun-freebsd-aarch64@1.3.14': + optional: true + + '@oven/bun-freebsd-x64@1.3.14': + optional: true + + '@oven/bun-linux-aarch64-android@1.3.14': + optional: true + + '@oven/bun-linux-aarch64-musl@1.3.14': + optional: true + + '@oven/bun-linux-aarch64@1.3.14': + optional: true + + '@oven/bun-linux-x64-android@1.3.14': + optional: true + + '@oven/bun-linux-x64-baseline@1.3.14': + optional: true + + '@oven/bun-linux-x64-musl-baseline@1.3.14': + optional: true + + '@oven/bun-linux-x64-musl@1.3.14': + optional: true + + '@oven/bun-linux-x64@1.3.14': + optional: true + + '@oven/bun-windows-aarch64@1.3.14': + optional: true + + '@oven/bun-windows-x64-baseline@1.3.14': + optional: true + + '@oven/bun-windows-x64@1.3.14': + optional: true + + '@oxc-project/types@0.142.0': {} + + '@parcel/watcher-android-arm64@2.6.0': + optional: true + + '@parcel/watcher-darwin-arm64@2.6.0': {} + + '@parcel/watcher-darwin-x64@2.6.0': {} + + '@parcel/watcher-freebsd-x64@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-musl@2.6.0': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.6.0': {} + + '@parcel/watcher-linux-arm64-musl@2.6.0': {} + + '@parcel/watcher-linux-x64-glibc@2.6.0': {} + + '@parcel/watcher-linux-x64-musl@2.6.0': {} + + '@parcel/watcher-wasm@2.5.6': dependencies: - detect-libc: 1.0.3 is-glob: 4.0.3 - micromatch: 4.0.7 + picomatch: 4.0.5 + + '@parcel/watcher-win32-arm64@2.6.0': + optional: true + + '@parcel/watcher-win32-x64@2.6.0': {} + + '@parcel/watcher@2.6.0(patch_hash=705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da)': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 node-addon-api: 7.1.1 + picomatch: 4.0.5 optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.0 - '@parcel/watcher-darwin-arm64': 2.5.0 - '@parcel/watcher-darwin-x64': 2.5.0 - '@parcel/watcher-freebsd-x64': 2.5.0 - '@parcel/watcher-linux-arm-glibc': 2.5.0 - '@parcel/watcher-linux-arm-musl': 2.5.0 - '@parcel/watcher-linux-arm64-glibc': 2.5.0 - '@parcel/watcher-linux-arm64-musl': 2.5.0 - '@parcel/watcher-linux-x64-glibc': 2.5.0 - '@parcel/watcher-linux-x64-musl': 2.5.0 - '@parcel/watcher-win32-arm64': 2.5.0 - '@parcel/watcher-win32-ia32': 2.5.0 - '@parcel/watcher-win32-x64': 2.5.0 + '@parcel/watcher-android-arm64': 2.6.0 + '@parcel/watcher-darwin-arm64': 2.6.0 + '@parcel/watcher-darwin-x64': 2.6.0 + '@parcel/watcher-freebsd-x64': 2.6.0 + '@parcel/watcher-linux-arm-glibc': 2.6.0 + '@parcel/watcher-linux-arm-musl': 2.6.0 + '@parcel/watcher-linux-arm64-glibc': 2.6.0 + '@parcel/watcher-linux-arm64-musl': 2.6.0 + '@parcel/watcher-linux-x64-glibc': 2.6.0 + '@parcel/watcher-linux-x64-musl': 2.6.0 + '@parcel/watcher-win32-arm64': 2.6.0 + '@parcel/watcher-win32-x64': 2.6.0 - '@pkgjs/parseargs@0.11.0': - optional: true - - '@playwright/test@1.49.0': + '@playwright/test@1.62.1': dependencies: - playwright: 1.49.0 + playwright: 1.62.1 - '@rollup/rollup-android-arm-eabi@4.20.0': + '@rolldown/binding-android-arm64@1.2.1': optional: true - '@rollup/rollup-android-arm-eabi@4.27.4': + '@rolldown/binding-darwin-arm64@1.2.1': optional: true - '@rollup/rollup-android-arm64@4.20.0': + '@rolldown/binding-darwin-x64@1.2.1': optional: true - '@rollup/rollup-android-arm64@4.27.4': + '@rolldown/binding-freebsd-x64@1.2.1': optional: true - '@rollup/rollup-darwin-arm64@4.20.0': + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': optional: true - '@rollup/rollup-darwin-arm64@4.27.4': + '@rolldown/binding-linux-arm64-gnu@1.2.1': optional: true - '@rollup/rollup-darwin-x64@4.20.0': + '@rolldown/binding-linux-arm64-musl@1.2.1': optional: true - '@rollup/rollup-darwin-x64@4.27.4': + '@rolldown/binding-linux-ppc64-gnu@1.2.1': optional: true - '@rollup/rollup-freebsd-arm64@4.27.4': + '@rolldown/binding-linux-s390x-gnu@1.2.1': optional: true - '@rollup/rollup-freebsd-x64@4.27.4': + '@rolldown/binding-linux-x64-gnu@1.2.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.20.0': + '@rolldown/binding-linux-x64-musl@1.2.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.27.4': + '@rolldown/binding-openharmony-arm64@1.2.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.20.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.27.4': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.20.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.27.4': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.20.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.27.4': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.20.0': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.27.4': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.20.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.27.4': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.20.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.27.4': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.20.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.27.4': - optional: true - - '@rollup/rollup-linux-x64-musl@4.20.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.27.4': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.20.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.27.4': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.20.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.27.4': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.20.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.27.4': - optional: true - - '@rtsao/scc@1.1.0': {} - - '@rushstack/eslint-patch@1.10.4': {} - - '@sindresorhus/merge-streams@2.3.0': {} - - '@swc/counter@0.1.3': {} - - '@swc/helpers@0.5.13': + '@rolldown/binding-wasm32-wasi@1.2.1': dependencies: - tslib: 2.8.0 + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.1': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.1': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@rspack/binding-darwin-arm64@2.0.2': + optional: true + + '@rspack/binding-darwin-x64@2.0.2': + optional: true + + '@rspack/binding-linux-arm64-gnu@2.0.2': + optional: true + + '@rspack/binding-linux-arm64-musl@2.0.2': + optional: true + + '@rspack/binding-linux-x64-gnu@2.0.2': + optional: true + + '@rspack/binding-linux-x64-musl@2.0.2': + optional: true + + '@rspack/binding-wasm32-wasi@2.0.2': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rspack/binding-win32-arm64-msvc@2.0.2': + optional: true + + '@rspack/binding-win32-ia32-msvc@2.0.2': + optional: true + + '@rspack/binding-win32-x64-msvc@2.0.2': + optional: true + + '@rspack/binding@2.0.2': + optionalDependencies: + '@rspack/binding-darwin-arm64': 2.0.2 + '@rspack/binding-darwin-x64': 2.0.2 + '@rspack/binding-linux-arm64-gnu': 2.0.2 + '@rspack/binding-linux-arm64-musl': 2.0.2 + '@rspack/binding-linux-x64-gnu': 2.0.2 + '@rspack/binding-linux-x64-musl': 2.0.2 + '@rspack/binding-wasm32-wasi': 2.0.2 + '@rspack/binding-win32-arm64-msvc': 2.0.2 + '@rspack/binding-win32-ia32-msvc': 2.0.2 + '@rspack/binding-win32-x64-msvc': 2.0.2 + + '@rspack/core@2.0.2(@swc/helpers@0.5.15)': + dependencies: + '@rspack/binding': 2.0.2 + optionalDependencies: + '@swc/helpers': 0.5.15 + + '@sindresorhus/merge-streams@4.0.0': {} + + '@standard-schema/spec@1.1.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 '@tailwindcss/aspect-ratio@0.4.2(tailwindcss@packages+tailwindcss)': dependencies: tailwindcss: link:packages/tailwindcss - '@tailwindcss/forms@0.5.9(tailwindcss@packages+tailwindcss)': + '@tailwindcss/forms@0.5.11(tailwindcss@packages+tailwindcss)': dependencies: mini-svg-data-uri: 1.4.4 tailwindcss: link:packages/tailwindcss - '@tailwindcss/typography@0.5.15(tailwindcss@packages+tailwindcss)': + '@tailwindcss/typography@0.5.20(tailwindcss@packages+tailwindcss)': dependencies: - lodash.castarray: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 tailwindcss: link:packages/tailwindcss - '@types/babel__core@7.20.5': + '@turbo/darwin-64@2.10.8': + optional: true + + '@turbo/darwin-arm64@2.10.8': + optional: true + + '@turbo/linux-64@2.10.8': + optional: true + + '@turbo/linux-arm64@2.10.8': + optional: true + + '@turbo/windows-64@2.10.8': + optional: true + + '@turbo/windows-arm64@2.10.8': + optional: true + + '@tybys/wasm-util@0.10.3': dependencies: - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 + tslib: 2.8.1 - '@types/babel__generator@7.6.8': + '@types/bun@1.3.14': dependencies: - '@babel/types': 7.25.2 + bun-types: 1.3.14 - '@types/babel__template@7.4.4': + '@types/chai@5.2.3': dependencies: - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - '@types/babel__traverse@7.20.6': - dependencies: - '@babel/types': 7.25.2 + '@types/deep-eql@4.0.2': {} - '@types/braces@3.0.4': {} + '@types/estree@1.0.8': {} - '@types/bun@1.1.13': - dependencies: - bun-types: 1.1.34 - - '@types/estree@1.0.5': {} - - '@types/estree@1.0.6': {} + '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} - - '@types/node@20.12.14': + '@types/node@22.20.1': dependencies: - undici-types: 5.26.5 + undici-types: 6.21.0 - '@types/node@20.14.13': + '@types/node@25.9.1': dependencies: - undici-types: 5.26.5 + undici-types: 7.24.6 '@types/postcss-import@14.0.3': dependencies: - postcss: 8.4.41 + postcss: 8.5.25 - '@types/prop-types@15.7.13': {} - - '@types/react-dom@18.3.1': + '@types/react-dom@19.2.3(@types/react@19.2.15)': dependencies: - '@types/react': 18.3.12 + '@types/react': 19.2.15 - '@types/react@18.3.12': + '@types/react@19.2.15': dependencies: - '@types/prop-types': 15.7.13 - csstype: 3.1.3 + csstype: 3.2.3 - '@types/ws@8.5.12': + '@types/semver@7.8.0': {} + + '@vitejs/plugin-react@6.0.2(vite@8.2.0(@types/node@25.9.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@types/node': 20.14.13 + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.0(@types/node@25.9.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4)': + '@vitest/expect@4.1.10': dependencies: - '@eslint-community/regexpp': 4.11.1 - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/type-utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - '@typescript-eslint/utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.11.0 - eslint: 9.15.0(jiti@2.4.0) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)': + '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@eslint-community/regexpp': 4.11.1 - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/type-utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.11.0 - eslint: 9.15.0(jiti@2.4.0) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4)': - dependencies: - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.11.0 - debug: 4.3.7 - eslint: 9.15.0(jiti@2.4.0) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.11.0 - debug: 4.3.7 - eslint: 9.15.0(jiti@2.4.0) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.11.0': - dependencies: - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/visitor-keys': 8.11.0 - - '@typescript-eslint/type-utils@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4)': - dependencies: - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - eslint - - supports-color - - '@typescript-eslint/type-utils@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - eslint - - supports-color - - '@typescript-eslint/types@8.11.0': {} - - '@typescript-eslint/typescript-estree@8.11.0(typescript@5.5.4)': - dependencies: - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/visitor-keys': 8.11.0 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.11.0(typescript@5.6.3)': - dependencies: - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/visitor-keys': 8.11.0 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.15.0(jiti@2.4.0)) - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.5.4) - eslint: 9.15.0(jiti@2.4.0) - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.15.0(jiti@2.4.0)) - '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/types': 8.11.0 - '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3) - eslint: 9.15.0(jiti@2.4.0) - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@8.11.0': - dependencies: - '@typescript-eslint/types': 8.11.0 - eslint-visitor-keys: 3.4.3 - - '@vitejs/plugin-react@4.3.1(vite@6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0))': - dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) - '@types/babel__core': 7.20.5 - react-refresh: 0.14.2 - vite: 6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@2.0.5': - dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - tinyrainbow: 1.2.0 - - '@vitest/pretty-format@2.0.5': - dependencies: - tinyrainbow: 1.2.0 - - '@vitest/runner@2.0.5': - dependencies: - '@vitest/utils': 2.0.5 - pathe: 1.1.2 - - '@vitest/snapshot@2.0.5': - dependencies: - '@vitest/pretty-format': 2.0.5 - magic-string: 0.30.11 - pathe: 1.1.2 - - '@vitest/spy@2.0.5': - dependencies: - tinyspy: 3.0.0 - - '@vitest/utils@2.0.5': - dependencies: - '@vitest/pretty-format': 2.0.5 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 - loupe: 3.1.1 - tinyrainbow: 1.2.0 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - acorn-jsx@5.3.2(acorn@8.14.0): + '@vitest/pretty-format@4.1.10': dependencies: - acorn: 8.14.0 + tinyrainbow: 3.1.0 - acorn@8.14.0: {} + '@vitest/runner@4.1.10': + dependencies: + '@vitest/utils': 4.1.10 + pathe: 2.0.3 - ajv@6.12.6: + '@vitest/snapshot@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.10': {} + + '@vitest/utils@4.1.10': + dependencies: + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + acorn@8.16.0: {} + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-regex@5.0.1: {} - - ansi-regex@6.0.1: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 any-promise@1.3.0: {} anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.1 + picomatch: 2.3.2 arg@5.0.2: {} argparse@2.0.1: {} - aria-query@5.3.2: {} - - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - - array-includes@3.1.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 - - array-union@2.1.0: {} - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - assertion-error@2.0.1: {} - ast-types-flow@0.0.8: {} - - autoprefixer@10.4.20(postcss@8.4.47): + autoprefixer@10.5.0(postcss@8.5.25): dependencies: - browserslist: 4.24.2 - caniuse-lite: 1.0.30001669 - fraction.js: 4.3.7 - normalize-range: 0.1.2 + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.4.47 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 + baseline-browser-mapping@2.10.31: {} - axe-core@4.10.2: {} - - axobject-query@4.1.0: {} - - balanced-match@1.0.2: {} + before-after-hook@4.0.0: {} binary-extensions@2.3.0: {} - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.23.2: + browserslist@4.28.2: dependencies: - caniuse-lite: 1.0.30001669 - electron-to-chromium: 1.5.4 - node-releases: 2.0.18 - update-browserslist-db: 1.1.0(browserslist@4.23.2) + baseline-browser-mapping: 2.10.31 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 + update-browserslist-db: 1.2.3(browserslist@4.28.2) - browserslist@4.24.2: + buffer-from@1.1.2: {} + + bun-types@1.3.14: dependencies: - caniuse-lite: 1.0.30001669 - electron-to-chromium: 1.5.45 - node-releases: 2.0.18 - update-browserslist-db: 1.1.1(browserslist@4.24.2) + '@types/node': 25.9.1 - buffer-from@1.1.2: - optional: true - - bun-types@1.1.34: - dependencies: - '@types/node': 20.12.14 - '@types/ws': 8.5.12 - - bun@1.1.29: + bun@1.3.14: optionalDependencies: - '@oven/bun-darwin-aarch64': 1.1.29 - '@oven/bun-darwin-x64': 1.1.29 - '@oven/bun-darwin-x64-baseline': 1.1.29 - '@oven/bun-linux-aarch64': 1.1.29 - '@oven/bun-linux-x64': 1.1.29 - '@oven/bun-linux-x64-baseline': 1.1.29 - '@oven/bun-windows-x64': 1.1.29 - '@oven/bun-windows-x64-baseline': 1.1.29 + '@oven/bun-darwin-aarch64': 1.3.14 + '@oven/bun-darwin-x64': 1.3.14 + '@oven/bun-darwin-x64-baseline': 1.3.14 + '@oven/bun-freebsd-aarch64': 1.3.14 + '@oven/bun-freebsd-x64': 1.3.14 + '@oven/bun-linux-aarch64': 1.3.14 + '@oven/bun-linux-aarch64-android': 1.3.14 + '@oven/bun-linux-aarch64-musl': 1.3.14 + '@oven/bun-linux-x64': 1.3.14 + '@oven/bun-linux-x64-android': 1.3.14 + '@oven/bun-linux-x64-baseline': 1.3.14 + '@oven/bun-linux-x64-musl': 1.3.14 + '@oven/bun-linux-x64-musl-baseline': 1.3.14 + '@oven/bun-windows-aarch64': 1.3.14 + '@oven/bun-windows-x64': 1.3.14 + '@oven/bun-windows-x64-baseline': 1.3.14 - bundle-require@5.0.0(esbuild@0.23.0): + bundle-require@5.1.0(esbuild@0.27.7): dependencies: - esbuild: 0.23.0 + esbuild: 0.27.7 load-tsconfig: 0.2.5 - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - cac@6.7.14: {} - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - callsites@3.1.0: {} - camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001669: {} + caniuse-lite@1.0.30001793: {} - chai@5.1.1: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.1 - pathval: 2.0.0 + chai@6.2.2: {} - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - check-error@2.1.1: {} + chardet@2.1.1: {} chokidar@3.6.0: dependencies: @@ -7437,715 +4883,188 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chrome-trace-event@1.0.4: {} + + citty@0.2.2: {} + + cli-width@4.1.0: {} + client-only@0.0.1: {} - color-convert@1.9.3: + clipanion@4.0.0-rc.4(typanion@3.14.0): dependencies: - color-name: 1.1.3 + typanion: 3.14.0 - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 + colorette@2.0.20: {} - color-name@1.1.3: {} - - color-name@1.1.4: {} - - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - optional: true - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - optional: true - - commander@2.20.3: - optional: true + commander@2.20.3: {} commander@4.1.1: {} - concat-map@0.0.1: {} + confbox@0.1.8: {} - consola@3.2.3: {} + consola@3.4.2: {} + + content-type@2.0.0: {} convert-source-map@2.0.0: {} - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + cookie-es@1.2.3: {} - cross-spawn@7.0.6: + crossws@0.3.5: dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + uncrypto: 0.1.3 + + crossws@0.4.10: {} cssesc@3.0.0: {} - csstype@3.1.3: {} + csstype@3.2.3: {} - damerau-levenshtein@1.0.8: {} - - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - debug@3.2.7: + debug@4.4.3: dependencies: ms: 2.1.3 - debug@4.3.6: - dependencies: - ms: 2.1.2 + dedent@1.7.2: {} - debug@4.3.7: - dependencies: - ms: 2.1.3 + defu@6.1.7: {} - dedent@1.5.3: {} - - deep-eql@5.0.2: {} - - deep-is@0.1.4: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 + destr@2.0.5: {} detect-libc@1.0.3: {} - detect-libc@2.0.3: - optional: true + detect-libc@2.1.2: {} didyoumean@1.2.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 + electron-to-chromium@1.5.361: {} - eastasianwidth@0.2.0: {} + emnapi@1.11.3(node-addon-api@8.7.0): + optionalDependencies: + node-addon-api: 8.7.0 - electron-to-chromium@1.5.4: {} - - electron-to-chromium@1.5.45: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - enhanced-resolve@5.17.1: + enhanced-resolve@5.24.5: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.1 - - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.2 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 + tapable: 2.3.3 es-errors@1.3.0: {} - es-iterator-helpers@1.1.0: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.3 - safe-array-concat: 1.1.2 + es-module-lexer@2.1.0: {} - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 + es-toolkit@1.50.0: {} - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - - esbuild@0.21.5: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.23.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 - - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - optional: true - - esbuild@0.24.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.0 - '@esbuild/android-arm': 0.24.0 - '@esbuild/android-arm64': 0.24.0 - '@esbuild/android-x64': 0.24.0 - '@esbuild/darwin-arm64': 0.24.0 - '@esbuild/darwin-x64': 0.24.0 - '@esbuild/freebsd-arm64': 0.24.0 - '@esbuild/freebsd-x64': 0.24.0 - '@esbuild/linux-arm': 0.24.0 - '@esbuild/linux-arm64': 0.24.0 - '@esbuild/linux-ia32': 0.24.0 - '@esbuild/linux-loong64': 0.24.0 - '@esbuild/linux-mips64el': 0.24.0 - '@esbuild/linux-ppc64': 0.24.0 - '@esbuild/linux-riscv64': 0.24.0 - '@esbuild/linux-s390x': 0.24.0 - '@esbuild/linux-x64': 0.24.0 - '@esbuild/netbsd-x64': 0.24.0 - '@esbuild/openbsd-arm64': 0.24.0 - '@esbuild/openbsd-x64': 0.24.0 - '@esbuild/sunos-x64': 0.24.0 - '@esbuild/win32-arm64': 0.24.0 - '@esbuild/win32-ia32': 0.24.0 - '@esbuild/win32-x64': 0.24.0 - - escalade@3.1.2: {} + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-next@15.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4): - dependencies: - '@next/eslint-plugin-next': 15.0.3 - '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-jsx-a11y: 6.10.1(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-react: 7.37.2(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-react-hooks: 5.0.0(eslint@9.15.0(jiti@2.4.0)) - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - - eslint-config-next@15.0.3(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3): - dependencies: - '@next/eslint-plugin-next': 15.0.3 - '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-jsx-a11y: 6.10.1(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-react: 7.37.2(eslint@9.15.0(jiti@2.4.0)) - eslint-plugin-react-hooks: 5.0.0(eslint@9.15.0(jiti@2.4.0)) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.15.1 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.3.7 - enhanced-resolve: 5.17.1 - eslint: 9.15.0(jiti@2.4.0) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)) - fast-glob: 3.3.2 - get-tsconfig: 4.8.1 - is-bun-module: 1.2.1 - is-glob: 4.0.3 - optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@9.15.0(jiti@2.4.0)) - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.3.7 - enhanced-resolve: 5.17.1 - eslint: 9.15.0(jiti@2.4.0) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)) - fast-glob: 3.3.2 - get-tsconfig: 4.8.1 - is-bun-module: 1.2.1 - is-glob: 4.0.3 - optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.15.0(jiti@2.4.0)) - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.3)(eslint@9.15.0(jiti@2.4.0)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)) - hasown: 2.0.2 - is-core-module: 2.15.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - string.prototype.trimend: 1.0.8 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.5.4) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint@9.15.0(jiti@2.4.0)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.15.0(jiti@2.4.0) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.15.0(jiti@2.4.0)))(eslint@9.15.0(jiti@2.4.0)) - hasown: 2.0.2 - is-core-module: 2.15.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - string.prototype.trimend: 1.0.8 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.15.0(jiti@2.4.0))(typescript@5.6.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jsx-a11y@6.10.1(eslint@9.15.0(jiti@2.4.0)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.8 - array.prototype.flatmap: 1.3.2 - ast-types-flow: 0.0.8 - axe-core: 4.10.2 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - es-iterator-helpers: 1.1.0 - eslint: 9.15.0(jiti@2.4.0) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.0.3 - string.prototype.includes: 2.0.1 - - eslint-plugin-react-hooks@5.0.0(eslint@9.15.0(jiti@2.4.0)): - dependencies: - eslint: 9.15.0(jiti@2.4.0) - - eslint-plugin-react@7.37.2(eslint@9.15.0(jiti@2.4.0)): - dependencies: - array-includes: 3.1.8 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.1.0 - eslint: 9.15.0(jiti@2.4.0) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.8 - object.fromentries: 2.0.8 - object.values: 1.2.0 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.11 - string.prototype.repeat: 1.0.0 - - eslint-scope@8.2.0: + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.0: {} - - eslint@9.15.0(jiti@2.4.0): - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.15.0(jiti@2.4.0)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.0 - '@eslint/core': 0.9.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.15.0 - '@eslint/plugin-kit': 0.2.3 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.3.7 - escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.4.0 - transitivePeerDependencies: - - supports-color - - espree@10.3.0: - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 + estraverse: 4.3.0 esrecurse@4.3.0: dependencies: estraverse: 5.3.0 + estraverse@4.3.0: {} + estraverse@5.3.0: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.9 - esutils@2.0.3: {} + events@3.3.0: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 + expect-type@1.3.0: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + fast-content-type-parse@3.0.0: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.1: + fast-equals@5.4.0: {} + + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 - fast-glob@3.3.2: + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.7 + fast-string-truncated-width: 3.0.3 - fast-json-stable-stringify@2.1.0: {} + fast-stringify@4.0.0: {} - fast-levenshtein@2.0.6: {} + fast-uri@3.1.2: {} - fastq@1.17.1: + fast-wrap-ansi@0.2.2: dependencies: - reusify: 1.0.4 + fast-string-width: 3.0.2 - file-entry-cache@8.0.0: + fastq@1.20.1: dependencies: - flat-cache: 4.0.1 + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-up-simple@1.0.0: {} + find-up-simple@1.0.1: {} - find-up@5.0.0: + fix-dts-default-cjs-exports@1.0.1: dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.60.4 - flat-cache@4.0.1: - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - - flatted@3.3.1: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - - foreground-child@3.3.0: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - - fraction.js@4.3.7: {} + fraction.js@5.3.4: {} fsevents@2.3.2: optional: true @@ -8155,40 +5074,7 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} - - gensync@1.0.0-beta.2: {} - - get-func-name@2.0.2: {} - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - - get-stream@6.0.1: {} - - get-stream@8.0.1: {} - - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 + get-port-please@3.2.0: {} glob-parent@5.1.2: dependencies: @@ -8198,596 +5084,335 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: + globby@16.2.2: dependencies: - foreground-child: 3.3.0 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - globals@11.12.0: {} - - globals@14.0.0: {} - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - globby@14.0.2: - dependencies: - '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 - ignore: 5.3.1 - path-type: 5.0.0 + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + is-path-inside: 4.0.0 slash: 5.1.0 - unicorn-magic: 0.1.0 - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 + unicorn-magic: 0.4.0 graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - - has-bigints@1.0.2: {} - - has-flag@3.0.0: {} + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - - hasown@2.0.2: + hasown@2.0.3: dependencies: function-bind: 1.1.2 - human-signals@2.1.0: {} + http-shutdown@1.2.2: {} - human-signals@5.0.0: {} - - ignore@5.3.1: {} - - ignore@5.3.2: {} - - import-fresh@3.3.0: + iconv-lite@0.7.2: dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 + safer-buffer: 2.1.2 - imurmurhash@0.1.4: {} + ignore@7.0.5: {} - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-arrayish@0.3.2: - optional: true - - is-async-function@2.0.0: - dependencies: - has-tostringtag: 1.0.2 - - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 + iron-webcrypto@1.2.1: {} is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.1.2: + is-core-module@2.16.2: dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-bun-module@1.2.1: - dependencies: - semver: 7.6.3 - - is-callable@1.2.7: {} - - is-core-module@2.15.0: - dependencies: - hasown: 2.0.2 - - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 + hasown: 2.0.3 is-extglob@2.1.1: {} - is-finalizationregistry@1.0.2: - dependencies: - call-bind: 1.0.7 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.0.10: - dependencies: - has-tostringtag: 1.0.2 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - is-number@7.0.0: {} - is-regex@1.1.4: + is-path-inside@4.0.0: {} + + jest-worker@27.5.1: dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 + '@types/node': 25.9.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 - is-set@2.0.3: {} + jiti@1.21.7: {} - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - - is-stream@2.0.1: {} - - is-stream@3.0.0: {} - - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-weakmap@2.0.2: {} - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 - - is-weakset@2.0.3: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - isarray@2.0.5: {} - - isexe@2.0.0: {} - - iterator.prototype@1.1.3: - dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.6 - set-function-name: 2.0.2 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jiti@1.21.6: {} - - jiti@2.4.0: {} + jiti@2.7.0: {} joycon@3.1.1: {} - js-tokens@4.0.0: {} - - js-yaml@4.1.0: + js-yaml@4.3.1: dependencies: argparse: 2.0.1 - jsesc@2.5.2: {} + json-schema-traverse@1.0.0: {} - json-buffer@3.0.1: {} + json-with-bigint@3.5.8: {} - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - json5@2.2.3: {} - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.2 - object.assign: 4.1.5 - object.values: 1.2.0 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lightningcss-darwin-arm64@1.26.0: {} - - lightningcss-darwin-x64@1.26.0: {} - - lightningcss-freebsd-x64@1.26.0: + lightningcss-android-arm64@1.33.0: optional: true - lightningcss-linux-arm-gnueabihf@1.26.0: + lightningcss-darwin-arm64@1.33.0: {} + + lightningcss-darwin-x64@1.33.0: {} + + lightningcss-freebsd-x64@1.33.0: optional: true - lightningcss-linux-arm64-gnu@1.26.0: {} - - lightningcss-linux-arm64-musl@1.26.0: {} - - lightningcss-linux-x64-gnu@1.26.0: {} - - lightningcss-linux-x64-musl@1.26.0: {} - - lightningcss-win32-arm64-msvc@1.26.0: + lightningcss-linux-arm-gnueabihf@1.33.0: optional: true - lightningcss-win32-x64-msvc@1.26.0: {} + lightningcss-linux-arm64-gnu@1.33.0: {} - lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4): + lightningcss-linux-arm64-musl@1.33.0: {} + + lightningcss-linux-x64-gnu@1.33.0: {} + + lightningcss-linux-x64-musl@1.33.0: {} + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: {} + + lightningcss@1.33.0(patch_hash=1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930): dependencies: - detect-libc: 1.0.3 + detect-libc: 2.1.2 optionalDependencies: - lightningcss-darwin-arm64: 1.26.0 - lightningcss-darwin-x64: 1.26.0 - lightningcss-freebsd-x64: 1.26.0 - lightningcss-linux-arm-gnueabihf: 1.26.0 - lightningcss-linux-arm64-gnu: 1.26.0 - lightningcss-linux-arm64-musl: 1.26.0 - lightningcss-linux-x64-gnu: 1.26.0 - lightningcss-linux-x64-musl: 1.26.0 - lightningcss-win32-arm64-msvc: 1.26.0 - lightningcss-win32-x64-msvc: 1.26.0 + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 - lilconfig@2.1.0: {} - - lilconfig@3.1.2: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} + listhen@1.10.1(@parcel/watcher@2.6.0(patch_hash=705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da)): + dependencies: + '@parcel/watcher-wasm': 2.5.6 + citty: 0.2.2 + consola: 3.4.2 + crossws: 0.4.10 + defu: 6.1.7 + get-port-please: 3.2.0 + h3: 1.15.11 + http-shutdown: 1.2.2 + jiti: 2.7.0 + node-forge: 1.4.0 + pathe: 2.0.3 + std-env: 4.2.0 + tinyclip: 0.1.15 + ufo: 1.6.4 + untun: 0.2.2 + uqr: 0.1.3 + optionalDependencies: + '@parcel/watcher': 2.6.0(patch_hash=705ce75ccea54337110c4d7fe0a8b421658ea0c23e1a77c1fa890a86041179da) + transitivePeerDependencies: + - srvx + load-tsconfig@0.2.5: {} - locate-path@6.0.0: + magic-string@0.30.21: dependencies: - p-locate: 5.0.0 + '@jridgewell/sourcemap-codec': 1.5.5 - lodash.castarray@4.4.0: {} - - lodash.isplainobject@4.0.6: {} - - lodash.merge@4.6.2: {} - - lodash.sortby@4.7.0: {} - - loose-envify@1.4.0: + magic-string@1.1.0: dependencies: - js-tokens: 4.0.0 - - loupe@3.1.1: - dependencies: - get-func-name: 2.0.2 - - lru-cache@10.4.3: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - magic-string@0.30.11: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 merge-stream@2.0.0: {} merge2@1.4.1: {} - micro-memoize@4.1.2: {} + micro-memoize@5.1.1: + dependencies: + fast-equals: 5.4.0 + fast-stringify: 4.0.0 - micromatch@4.0.7: + micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 - mimic-fn@2.1.0: {} - - mimic-fn@4.0.0: {} + mime-db@1.54.0: {} mini-svg-data-uri@1.4.4: {} - minimatch@3.1.2: + minimizer-webpack-plugin@5.6.1(esbuild@0.27.7)(postcss@8.5.25)(webpack@5.109.2(esbuild@0.27.7)(postcss@8.5.25)): dependencies: - brace-expansion: 1.1.11 + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.48.0 + webpack: 5.109.2(esbuild@0.27.7)(postcss@8.5.25) + optionalDependencies: + esbuild: 0.27.7 + postcss: 8.5.25 - minimatch@9.0.5: + mlly@1.8.2: dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} - - minipass@7.1.2: {} + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 mri@1.2.0: {} - ms@2.1.2: {} - ms@2.1.3: {} + mute-stream@3.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} + nanoid@3.3.12: {} - natural-compare@1.4.0: {} + nanoid@3.3.16: {} - next@15.0.3(@playwright/test@1.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + neo-async@2.6.2: {} + + next@16.2.7(@playwright/test@1.62.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@next/env': 15.0.3 - '@swc/counter': 0.1.3 - '@swc/helpers': 0.5.13 - busboy: 1.6.0 - caniuse-lite: 1.0.30001669 + '@next/env': 16.2.7 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.31 + caniuse-lite: 1.0.30001793 postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(react@18.3.1) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: - '@next/swc-darwin-arm64': 15.0.3 - '@next/swc-darwin-x64': 15.0.3 - '@next/swc-linux-arm64-gnu': 15.0.3 - '@next/swc-linux-arm64-musl': 15.0.3 - '@next/swc-linux-x64-gnu': 15.0.3 - '@next/swc-linux-x64-musl': 15.0.3 - '@next/swc-win32-arm64-msvc': 15.0.3 - '@next/swc-win32-x64-msvc': 15.0.3 - '@playwright/test': 1.49.0 - sharp: 0.33.5 + '@next/swc-darwin-arm64': 16.2.7 + '@next/swc-darwin-x64': 16.2.7 + '@next/swc-linux-arm64-gnu': 16.2.7 + '@next/swc-linux-arm64-musl': 16.2.7 + '@next/swc-linux-x64-gnu': 16.2.7 + '@next/swc-linux-x64-musl': 16.2.7 + '@next/swc-win32-arm64-msvc': 16.2.7 + '@next/swc-win32-x64-msvc': 16.2.7 + '@playwright/test': 1.62.1 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros node-addon-api@7.1.1: {} - node-addon-api@8.2.2: {} + node-addon-api@8.7.0: {} - node-gyp-build@4.8.2: {} + node-forge@1.4.0: {} - node-releases@2.0.18: {} + node-gyp-build@4.8.4: {} + + node-mock-http@1.0.4: {} + + node-releases@2.0.46: {} normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - object-assign@4.1.1: {} object-hash@3.0.0: {} - object-inspect@1.13.2: {} + obug@2.1.1: {} - object-keys@1.1.1: {} - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - - object.entries@1.1.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - - object.values@1.2.0: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - package-json-from-dist@1.0.1: {} + obug@2.1.4: {} package-up@5.0.0: dependencies: - find-up-simple: 1.0.0 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} + find-up-simple: 1.0.1 path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-type@4.0.0: {} - - path-type@5.0.0: {} - - pathe@1.1.2: {} - - pathval@2.0.0: {} + pathe@2.0.3: {} picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + picomatch@4.0.5: {} pify@2.3.0: {} - pirates@4.0.6: {} + pirates@4.0.7: {} - playwright-core@1.49.0: {} - - playwright@1.49.0: + pkg-types@1.3.1: dependencies: - playwright-core: 1.49.0 + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + playwright-core@1.62.1: {} + + playwright@1.62.1: + dependencies: + playwright-core: 1.62.1 optionalDependencies: fsevents: 2.3.2 - possible-typed-array-names@1.0.0: {} - - postcss-import@15.1.0(postcss@8.4.47): + postcss-import@15.1.0(postcss@8.5.16): dependencies: - postcss: 8.4.47 + postcss: 8.5.16 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.12 - postcss-import@16.1.0(postcss@8.4.41): + postcss-import@16.1.1(postcss@8.5.25): dependencies: - postcss: 8.4.41 + postcss: 8.5.25 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.8 + resolve: 1.22.12 - postcss-import@16.1.0(postcss@8.4.49): - dependencies: - postcss: 8.4.49 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.8 - - postcss-js@4.0.1(postcss@8.4.47): + postcss-js@4.1.0(postcss@8.5.16): dependencies: camelcase-css: 2.0.1 - postcss: 8.4.47 + postcss: 8.5.16 - postcss-load-config@4.0.2(postcss@8.4.47): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.16)(yaml@2.9.0): dependencies: - lilconfig: 3.1.2 - yaml: 2.6.0 + lilconfig: 3.1.3 optionalDependencies: - postcss: 8.4.47 + jiti: 1.21.7 + postcss: 8.5.16 + yaml: 2.9.0 - postcss-load-config@6.0.1(jiti@2.4.0)(postcss@8.4.49)(tsx@4.19.1)(yaml@2.6.0): + postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.25)(yaml@2.9.0): dependencies: - lilconfig: 3.1.2 + lilconfig: 3.1.3 optionalDependencies: - jiti: 2.4.0 - postcss: 8.4.49 - tsx: 4.19.1 - yaml: 2.6.0 + jiti: 2.7.0 + postcss: 8.5.25 + yaml: 2.9.0 - postcss-nested@6.2.0(postcss@8.4.47): + postcss-nested@6.2.0(postcss@8.5.16): dependencies: - postcss: 8.4.47 + postcss: 8.5.16 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -8800,7 +5425,7 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.0.0: + postcss-selector-parser@7.1.4: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -8809,71 +5434,57 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.41: + postcss@8.5.16: dependencies: - nanoid: 3.3.7 - picocolors: 1.1.1 - source-map-js: 1.2.0 - - postcss@8.4.47: - dependencies: - nanoid: 3.3.7 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.4.49: + postcss@8.5.25: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 - prelude-ls@1.2.1: {} - - prettier-plugin-embed@0.4.15: + prettier-plugin-embed@0.5.1: dependencies: - '@types/estree': 1.0.5 - dedent: 1.5.3 - micro-memoize: 4.1.2 + '@types/estree': 1.0.9 + dedent: 1.7.2 + micro-memoize: 5.1.1 package-up: 5.0.0 - tiny-jsonc: 1.0.1 - type-fest: 4.24.0 + tiny-jsonc: 1.0.2 + type-fest: 5.6.0 transitivePeerDependencies: - babel-plugin-macros - prettier-plugin-organize-imports@4.0.0(prettier@3.3.3)(typescript@5.5.4): + prettier-plugin-organize-imports@4.3.0(prettier@3.9.6)(typescript@5.9.3): dependencies: - prettier: 3.3.3 - typescript: 5.5.4 + prettier: 3.9.6 + typescript: 5.9.3 - prettier@3.3.3: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - punycode@2.3.1: {} + prettier@3.9.6: {} queue-microtask@1.2.3: {} - react-dom@18.3.1(react@18.3.1): + radix3@1.1.2: {} + + react-dom@19.2.6(react@19.2.6): dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react: 19.2.6 + scheduler: 0.27.0 - react-is@16.13.1: {} - - react-refresh@0.14.2: {} - - react@18.3.1: + react-dom@19.2.7(react@19.2.7): dependencies: - loose-envify: 1.4.0 + react: 19.2.7 + scheduler: 0.27.0 + + react@19.2.6: {} + + react@19.2.7: {} read-cache@1.0.0: dependencies: @@ -8881,344 +5492,206 @@ snapshots: readdirp@3.6.0: dependencies: - picomatch: 2.3.1 + picomatch: 2.3.2 - reflect.getprototypeof@1.0.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - which-builtin-type: 1.1.4 + readdirp@4.1.2: {} - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 - - resolve-from@4.0.0: {} + require-from-string@2.0.2: {} resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - - resolve@1.22.8: + resolve@1.22.12: dependencies: - is-core-module: 2.15.0 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.15.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} - reusify@1.0.4: {} - - rollup@4.20.0: + rolldown@1.2.1: dependencies: - '@types/estree': 1.0.5 + '@oxc-project/types': 0.142.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.20.0 - '@rollup/rollup-android-arm64': 4.20.0 - '@rollup/rollup-darwin-arm64': 4.20.0 - '@rollup/rollup-darwin-x64': 4.20.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.20.0 - '@rollup/rollup-linux-arm-musleabihf': 4.20.0 - '@rollup/rollup-linux-arm64-gnu': 4.20.0 - '@rollup/rollup-linux-arm64-musl': 4.20.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.20.0 - '@rollup/rollup-linux-riscv64-gnu': 4.20.0 - '@rollup/rollup-linux-s390x-gnu': 4.20.0 - '@rollup/rollup-linux-x64-gnu': 4.20.0 - '@rollup/rollup-linux-x64-musl': 4.20.0 - '@rollup/rollup-win32-arm64-msvc': 4.20.0 - '@rollup/rollup-win32-ia32-msvc': 4.20.0 - '@rollup/rollup-win32-x64-msvc': 4.20.0 - fsevents: 2.3.3 + '@rolldown/binding-android-arm64': 1.2.1 + '@rolldown/binding-darwin-arm64': 1.2.1 + '@rolldown/binding-darwin-x64': 1.2.1 + '@rolldown/binding-freebsd-x64': 1.2.1 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.1 + '@rolldown/binding-linux-arm64-gnu': 1.2.1 + '@rolldown/binding-linux-arm64-musl': 1.2.1 + '@rolldown/binding-linux-ppc64-gnu': 1.2.1 + '@rolldown/binding-linux-s390x-gnu': 1.2.1 + '@rolldown/binding-linux-x64-gnu': 1.2.1 + '@rolldown/binding-linux-x64-musl': 1.2.1 + '@rolldown/binding-openharmony-arm64': 1.2.1 + '@rolldown/binding-wasm32-wasi': 1.2.1 + '@rolldown/binding-win32-arm64-msvc': 1.2.1 + '@rolldown/binding-win32-x64-msvc': 1.2.1 - rollup@4.27.4: + rollup@4.60.4: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.27.4 - '@rollup/rollup-android-arm64': 4.27.4 - '@rollup/rollup-darwin-arm64': 4.27.4 - '@rollup/rollup-darwin-x64': 4.27.4 - '@rollup/rollup-freebsd-arm64': 4.27.4 - '@rollup/rollup-freebsd-x64': 4.27.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.27.4 - '@rollup/rollup-linux-arm-musleabihf': 4.27.4 - '@rollup/rollup-linux-arm64-gnu': 4.27.4 - '@rollup/rollup-linux-arm64-musl': 4.27.4 - '@rollup/rollup-linux-powerpc64le-gnu': 4.27.4 - '@rollup/rollup-linux-riscv64-gnu': 4.27.4 - '@rollup/rollup-linux-s390x-gnu': 4.27.4 - '@rollup/rollup-linux-x64-gnu': 4.27.4 - '@rollup/rollup-linux-x64-musl': 4.27.4 - '@rollup/rollup-win32-arm64-msvc': 4.27.4 - '@rollup/rollup-win32-ia32-msvc': 4.27.4 - '@rollup/rollup-win32-x64-msvc': 4.27.4 + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - safe-array-concat@1.1.2: + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + schema-utils@4.3.3: dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) - safe-regex-test@1.0.3: + semver@7.8.5: {} + + sharp@0.34.5: dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - - scheduler@0.23.2: - dependencies: - loose-envify: 1.4.0 - - semver@6.3.1: {} - - semver@7.6.3: {} - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - sharp@0.33.5: - dependencies: - color: 4.2.3 - detect-libc: 2.0.3 - semver: 7.6.3 + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.2 - siginfo@2.0.0: {} - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 - optional: true - - slash@3.0.0: {} - slash@5.1.0: {} - source-map-js@1.2.0: {} - source-map-js@1.2.1: {} source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - optional: true - source-map@0.6.1: - optional: true + source-map@0.6.1: {} - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map@0.7.6: {} stackback@0.0.2: {} - std-env@3.7.0: {} + std-env@4.1.0: {} - streamsearch@1.1.0: {} + std-env@4.2.0: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - - string.prototype.matchall@4.0.11: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 - set-function-name: 2.0.2 - side-channel: 1.0.6 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.3 - - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.0.1 - - strip-bom@3.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-final-newline@3.0.0: {} - - strip-json-comments@3.1.1: {} - - styled-jsx@5.1.6(react@18.3.1): + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 - react: 18.3.1 + react: 19.2.7 - sucrase@3.35.0: + sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 + tinyglobby: 0.2.16 ts-interface-checker: 0.1.13 - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: + supports-color@8.1.1: dependencies: has-flag: 4.0.0 supports-preserve-symlinks-flag@1.0.0: {} - tailwindcss@3.4.14: + tagged-tag@1.0.0: {} + + tailwindcss@3.4.19(yaml@2.9.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.21.6 - lilconfig: 2.1.0 - micromatch: 4.0.7 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.4.47 - postcss-import: 15.1.0(postcss@8.4.47) - postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47) - postcss-nested: 6.2.0(postcss@8.4.47) + postcss: 8.5.16 + postcss-import: 15.1.0(postcss@8.5.16) + postcss-js: 4.1.0(postcss@8.5.16) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.16)(yaml@2.9.0) + postcss-nested: 6.2.0(postcss@8.5.16) postcss-selector-parser: 6.1.2 - resolve: 1.22.8 - sucrase: 3.35.0 + resolve: 1.22.12 + sucrase: 3.35.1 transitivePeerDependencies: - - ts-node + - tsx + - yaml - tapable@2.2.1: {} + tapable@2.3.3: {} - terser@5.31.6: + terser@5.48.0: dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.0 + '@jridgewell/source-map': 0.3.11 + acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 - optional: true thenify-all@1.6.0: dependencies: @@ -9228,347 +5701,231 @@ snapshots: dependencies: any-promise: 1.3.0 - tiny-jsonc@1.0.1: {} + tiny-jsonc@1.0.2: {} tinybench@2.9.0: {} - tinypool@1.0.0: {} + tinyclip@0.1.15: {} - tinyrainbow@1.2.0: {} + tinyexec@0.3.2: {} - tinyspy@3.0.0: {} + tinyexec@1.1.2: {} - to-fast-properties@2.0.0: {} + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tree-kill@1.2.2: {} - tree-sitter-javascript@0.23.1(tree-sitter@0.22.1): + tree-sitter-javascript@0.23.1(tree-sitter@0.25.1): dependencies: - node-addon-api: 8.2.2 - node-gyp-build: 4.8.2 + node-addon-api: 8.7.0 + node-gyp-build: 4.8.4 optionalDependencies: - tree-sitter: 0.22.1 + tree-sitter: 0.25.1 - tree-sitter-typescript@0.23.2(tree-sitter@0.22.1): + tree-sitter-typescript@0.23.2(tree-sitter@0.25.1): dependencies: - node-addon-api: 8.2.2 - node-gyp-build: 4.8.2 - tree-sitter-javascript: 0.23.1(tree-sitter@0.22.1) + node-addon-api: 8.7.0 + node-gyp-build: 4.8.4 + tree-sitter-javascript: 0.23.1(tree-sitter@0.25.1) optionalDependencies: - tree-sitter: 0.22.1 + tree-sitter: 0.25.1 - tree-sitter@0.22.1: + tree-sitter@0.25.1: dependencies: - node-addon-api: 8.2.2 - node-gyp-build: 4.8.2 - - ts-api-utils@1.3.0(typescript@5.5.4): - dependencies: - typescript: 5.5.4 - - ts-api-utils@1.3.0(typescript@5.6.3): - dependencies: - typescript: 5.6.3 + node-addon-api: 8.7.0 + node-gyp-build: 4.8.4 ts-interface-checker@0.1.13: {} - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 + tslib@2.8.1: {} - tslib@2.8.0: {} - - tsup@8.2.4(jiti@2.4.0)(postcss@8.4.49)(tsx@4.19.1)(typescript@5.5.4)(yaml@2.6.0): + tsup@8.5.1(jiti@2.7.0)(postcss@8.5.25)(typescript@5.9.3)(yaml@2.9.0): dependencies: - bundle-require: 5.0.0(esbuild@0.23.0) + bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 - chokidar: 3.6.0 - consola: 3.2.3 - debug: 4.3.6 - esbuild: 0.23.0 - execa: 5.1.1 - globby: 11.1.0 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.7 + fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.4.0)(postcss@8.4.49)(tsx@4.19.1)(yaml@2.6.0) + postcss-load-config: 6.0.1(jiti@2.7.0)(postcss@8.5.25)(yaml@2.9.0) resolve-from: 5.0.0 - rollup: 4.20.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 + rollup: 4.60.4 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.16 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.49 - typescript: 5.5.4 + postcss: 8.5.25 + typescript: 5.9.3 transitivePeerDependencies: - jiti - supports-color - tsx - yaml - tsx@4.19.1: - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.1 + turbo@2.10.8: optionalDependencies: - fsevents: 2.3.3 - optional: true + '@turbo/darwin-64': 2.10.8 + '@turbo/darwin-arm64': 2.10.8 + '@turbo/linux-64': 2.10.8 + '@turbo/linux-arm64': 2.10.8 + '@turbo/windows-64': 2.10.8 + '@turbo/windows-arm64': 2.10.8 - turbo-darwin-64@2.2.3: - optional: true + typanion@3.14.0: {} - turbo-darwin-arm64@2.2.3: - optional: true - - turbo-linux-64@2.2.3: - optional: true - - turbo-linux-arm64@2.2.3: - optional: true - - turbo-windows-64@2.2.3: - optional: true - - turbo-windows-arm64@2.2.3: - optional: true - - turbo@2.2.3: - optionalDependencies: - turbo-darwin-64: 2.2.3 - turbo-darwin-arm64: 2.2.3 - turbo-linux-64: 2.2.3 - turbo-linux-arm64: 2.2.3 - turbo-windows-64: 2.2.3 - turbo-windows-arm64: 2.2.3 - - type-check@0.4.0: + type-fest@5.6.0: dependencies: - prelude-ls: 1.2.1 + tagged-tag: 1.0.0 - type-fest@4.24.0: {} + typescript@5.9.3: {} - typed-array-buffer@1.0.2: + typescript@6.0.3: {} + + ufo@1.6.4: {} + + uncrypto@0.1.3: {} + + undici-types@6.21.0: {} + + undici-types@7.24.6: {} + + unicorn-magic@0.4.0: {} + + universal-user-agent@7.0.3: {} + + untun@0.2.2: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - - typescript@5.5.4: {} - - typescript@5.6.3: {} - - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - - undici-types@5.26.5: {} - - unicorn-magic@0.1.0: {} - - update-browserslist-db@1.1.0(browserslist@4.23.2): - dependencies: - browserslist: 4.23.2 - escalade: 3.1.2 - picocolors: 1.1.1 - - update-browserslist-db@1.1.1(browserslist@4.24.2): - dependencies: - browserslist: 4.24.2 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 + uqr@0.1.3: {} util-deprecate@1.0.2: {} - vite-node@2.0.5(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6): + vite@8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: - cac: 6.7.14 - debug: 4.3.6 - pathe: 1.1.2 - tinyrainbow: 1.2.0 - vite: 5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite@5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.49 - rollup: 4.27.4 + lightningcss: 1.33.0(patch_hash=1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930) + picomatch: 4.0.5 + postcss: 8.5.25 + rolldown: 1.2.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.14.13 + '@types/node': 22.20.1 + esbuild: 0.27.7 fsevents: 2.3.3 - lightningcss: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) - terser: 5.31.6 + jiti: 2.7.0 + terser: 5.48.0 + yaml: 2.9.0 - vite@6.0.0(@types/node@20.14.13)(jiti@2.4.0)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6)(tsx@4.19.1)(yaml@2.6.0): + vite@8.2.0(@types/node@25.9.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: - esbuild: 0.24.0 - postcss: 8.4.49 - rollup: 4.27.4 + lightningcss: 1.33.0(patch_hash=1d4a8800d60d13d42887b88b3a86576df4b451670308145fb432ec8abbf40930) + picomatch: 4.0.5 + postcss: 8.5.25 + rolldown: 1.2.1 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 20.14.13 + '@types/node': 25.9.1 + esbuild: 0.27.7 fsevents: 2.3.3 - jiti: 2.4.0 - lightningcss: 1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4) - terser: 5.31.6 - tsx: 4.19.1 - yaml: 2.6.0 + jiti: 2.7.0 + terser: 5.48.0 + yaml: 2.9.0 - vitest@2.0.5(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6): + vitest@4.1.10(@types/node@22.20.1)(vite@8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - debug: 4.3.6 - execa: 8.0.1 - magic-string: 0.30.11 - pathe: 1.1.2 - std-env: 3.7.0 + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 tinybench: 2.9.0 - tinypool: 1.0.0 - tinyrainbow: 1.2.0 - vite: 5.4.0(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6) - vite-node: 2.0.5(@types/node@20.14.13)(lightningcss@1.26.0(patch_hash=5hwfyehqvg5wjb7mwtdvubqbl4))(terser@5.31.6) + tinyexec: 1.1.2 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.2.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 20.14.13 + '@types/node': 22.20.1 transitivePeerDependencies: - - less + - msw + + watchpack@2.5.2: + dependencies: + graceful-fs: 4.2.11 + + webpack-sources@3.5.1: {} + + webpack@5.109.2(esbuild@0.27.7)(postcss@8.5.25): + dependencies: + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + browserslist: 4.28.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.24.5 + es-module-lexer: 2.1.0 + eslint-scope: 5.1.1 + events: 3.3.0 + graceful-fs: 4.2.11 + mime-db: 1.54.0 + minimizer-webpack-plugin: 5.6.1(esbuild@0.27.7)(postcss@8.5.25)(webpack@5.109.2(esbuild@0.27.7)(postcss@8.5.25)) + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.3 + watchpack: 2.5.2 + webpack-sources: 3.5.1 + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - webidl-conversions@4.0.2: {} - - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-builtin-type@1.1.4: - dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.2 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.2 - which-typed-array: 1.1.15 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.3 - - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 + - postcss + - uglify-js why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - word-wrap@1.2.5: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - yallist@3.1.1: {} - - yaml@2.6.0: {} - - yocto-queue@0.1.0: {} + yaml@2.9.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4573bf90d..bcddef126 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,7 +5,50 @@ packages: - 'playgrounds/*' - 'integrations' +patchedDependencies: + '@napi-rs/cli@3.7.4': patches/@napi-rs__cli@3.7.4.patch + '@parcel/watcher@2.6.0': patches/@parcel__watcher@2.6.0.patch + lightningcss@1.33.0: patches/lightningcss@1.33.0.patch + catalog: - '@types/node': ^20.14.8 - lightningcss: ^1.26.0 - vite: ^6.0.0 + '@types/node': 22.20.1 + dedent: 1.7.2 + enhanced-resolve: ^5.24.5 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lightningcss: 1.33.0 + '@parcel/watcher-darwin-arm64': 2.6.0 + '@parcel/watcher-darwin-x64': 2.6.0 + '@parcel/watcher-linux-arm64-glibc': 2.6.0 + '@parcel/watcher-linux-arm64-musl': 2.6.0 + '@parcel/watcher-linux-x64-glibc': 2.6.0 + '@parcel/watcher-linux-x64-musl': 2.6.0 + '@parcel/watcher-win32-x64': 2.6.0 + '@parcel/watcher': 2.6.0 + postcss: ^8.5.25 + prettier: 3.9.6 + vite: ^8.2.0 + webpack: ^5.109.2 + +onlyBuiltDependencies: + - '@parcel/watcher' + - bun + - esbuild + - sharp + - tree-sitter + - tree-sitter-javascript + - tree-sitter-typescript + +allowBuilds: + '@parcel/watcher': true + bun: true + esbuild: true + sharp: true + tree-sitter: true + tree-sitter-javascript: true + tree-sitter-typescript: true diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7f466bd2d..f92020c65 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.80.1" +channel = "1.95.0" profile = "default" diff --git a/scripts/pack-packages.mjs b/scripts/pack-packages.mjs index 3bcf23bec..2b0b9f15d 100644 --- a/scripts/pack-packages.mjs +++ b/scripts/pack-packages.mjs @@ -21,30 +21,25 @@ let workspaces = new Map() for (let path of paths) { let pkg = await fs.readFile(path, 'utf8').then(JSON.parse) if (pkg.private) continue - workspaces.set(pkg.name, { version: pkg.version ?? '', dir: dirname(path) }) -} - -// Move napi artifacts into sub packages -const tailwindcssOxideRoot = path.join(root, 'crates', 'node') -for (let file of await fs.readdir(tailwindcssOxideRoot)) { - if (file.startsWith('tailwindcss-oxide.') && file.endsWith('.node')) { - let target = file.split('.')[1] - await fs.cp( - path.join(tailwindcssOxideRoot, file), - path.join(tailwindcssOxideRoot, 'npm', target, file), - ) - } + workspaces.set(pkg.name, { + version: pkg.version ?? '', + dir: dirname(path), + hasBundledDependencies: pkg.bundledDependencies?.length > 0 ?? false, + }) } // Clean dist folder await fs.rm(path.join(root, 'dist'), { recursive: true, force: true }) Promise.all( - [...workspaces.entries()].map(async ([name, { version, dir }]) => { + [...workspaces.entries()].map(async ([name, { dir, hasBundledDependencies }]) => { function pack() { return new Promise((resolve) => { + // `bundledDependencies` can only be packed with the hoisted node linker + // since pnpm v10. We opt into it for those packages only, instead of + // changing the node linker for the entire workspace. exec( - `pnpm pack --pack-gzip-level=0 --pack-destination="${path.join(root, 'dist').replace(/\\/g, '\\\\')}"`, + `pnpm pack --pack-gzip-level=0 --pack-destination="${path.join(root, 'dist').replace(/\\/g, '\\\\')}"${hasBundledDependencies ? ' --config.node-linker=hoisted' : ''}`, { cwd: dir }, (err, stdout, stderr) => { if (err) { diff --git a/scripts/release-channel.js b/scripts/release-channel.js new file mode 100644 index 000000000..15dc7df82 --- /dev/null +++ b/scripts/release-channel.js @@ -0,0 +1,20 @@ +// Given a version, figure out what the release channel is so that we can publish to the correct +// channel on npm. +// +// E.g.: +// +// 1.2.3 -> latest (default) +// 0.0.0-insiders.ffaa88 -> insiders +// 4.1.0-alpha.4 -> alpha + +let version = + process.argv[2] || + process.env.npm_package_version || + require('../packages/tailwindcss/package.json').version + +let match = /\d+\.\d+\.\d+-(.*)\.\d+/g.exec(version) +if (match) { + console.log(match[1]) +} else { + console.log('latest') +} diff --git a/scripts/release-notes.mjs b/scripts/release-notes.mjs index 289cb1d96..bb703c07b 100644 --- a/scripts/release-notes.mjs +++ b/scripts/release-notes.mjs @@ -1,5 +1,5 @@ // Given a version, figure out what the release notes are so that we can use this to pre-fill the -// relase notes on a GitHub release for the current version. +// release notes on a GitHub release for the current version. import * as fs from 'node:fs/promises' import * as path from 'node:path' diff --git a/scripts/version-packages.mjs b/scripts/version-packages.mjs index 2e3d93a3e..1641d5404 100644 --- a/scripts/version-packages.mjs +++ b/scripts/version-packages.mjs @@ -8,6 +8,7 @@ import prettier from 'prettier' const __dirname = path.dirname(url.fileURLToPath(import.meta.url)) const root = path.resolve(__dirname, '..') +const version = process.argv[2] || null // The known workspace is: @tailwindcss/oxide // All the workspaces in `crates/node/npm/*` should always be in sync with @@ -27,8 +28,9 @@ const syncedWorkspaces = new Map([ 'crates/node/npm/linux-arm64-musl', 'crates/node/npm/linux-x64-gnu', 'crates/node/npm/linux-x64-musl', - 'crates/node/npm/win32-x64-msvc', + 'crates/node/npm/wasm32-wasi', 'crates/node/npm/win32-arm64-msvc', + 'crates/node/npm/win32-x64-msvc', ], ], ['@tailwindcss/cli', ['packages/@tailwindcss-standalone']], @@ -46,80 +48,19 @@ for (let [name, paths] of syncedWorkspaces) { } } -exec('pnpm --silent --filter=!./playgrounds/* -r exec pwd', async (err, stdout) => { - if (err) { - console.error(err) - process.exit(1) - } +exec( + "pnpm --silent --filter='!./playgrounds/*' --filter='!./integrations' --filter='!./packages/internal-example-plugin' -r exec pwd", + async (err, stdout) => { + if (err) { + console.error(err) + process.exit(1) + } - let paths = stdout - .trim() - .split('\n') - .map((x) => path.resolve(x, 'package.json')) - // Workspaces that are in sync with another workspace should not be updated - // manually, they should be updated by updating the main workspace. - .filter((x) => !inverseSyncedWorkspaces.has(x)) - - let workspaces = new Map() - - // Track all the workspaces - for (let path of paths) { - let pkg = await fs.readFile(path, 'utf8').then(JSON.parse) - if (pkg.private) continue - workspaces.set(pkg.name, { version: pkg.version ?? '', path }) - } - - // Build the editable output - let lines = ['# Update the versions of the packages you want to change', ''] - for (let [name, info] of workspaces) { - lines.push(`${name}: ${info.version}`) - } - let output = lines.join('\n') - - // Edit the file - { - // Figure out which editor to use. - // - // In this case we still split on whitespace, because it can happen that the - // EDITOR env variable is configured as `code --wait`. This means that we - // want `code` as the editor, but `--wait` is one of the arguments. - let args = process.env.EDITOR.split(' ') - let editor = args.shift() - - // Create a temporary file which will be edited - let filepath = path.resolve(tmpdir(), `version-${randomUUID()}.txt`) - await fs.writeFile(filepath, output) - - // Edit the file, once the editor is closed, the file will be saved and we - // can read the changes - spawnSync(editor, [...args, filepath], { - stdio: 'inherit', - }) - - let newOutput = await fs.readFile(filepath, 'utf8').then((x) => x.trim().split('\n')) - - // Cleanup temporary file - await fs.unlink(filepath) - - // Update the package.json files - for (let line of newOutput) { - if (line[0] === '#') continue // Skip comment lines - if (line.trim() === '') continue // Skip empty lines - - let [name, version = ''] = line.split(':').map((x) => x.trim()) - - // Figure out all the paths to the package.json files that need to be - // updated with the new version - let paths = [ - // The package.json file of the main workspace - workspaces.get(name).path, - - // The package.json files of the workspaces that are in sync with the - // main workspace - ...(syncedWorkspaces.get(name) ?? []), - ] - - for (let pkgPath of paths) { + if (version !== null) { + for (let pkgPath of stdout + .trim() + .split('\n') + .map((x) => path.resolve(x, 'package.json'))) { let pkg = await fs.readFile(pkgPath, 'utf8').then(JSON.parse) let name = pkg.name if (version !== '') { @@ -138,8 +79,100 @@ exec('pnpm --silent --filter=!./playgrounds/* -r exec pwd', async (err, stdout) .then((x) => `${x.trim()}\n`), ) } - } - } - console.log('Done.') -}) + console.log('Done.') + return + } + + let paths = stdout + .trim() + .split('\n') + .map((x) => path.resolve(x, 'package.json')) + // Workspaces that are in sync with another workspace should not be updated + // manually, they should be updated by updating the main workspace. + .filter((x) => !inverseSyncedWorkspaces.has(x)) + + let workspaces = new Map() + + // Track all the workspaces + for (let path of paths) { + let pkg = await fs.readFile(path, 'utf8').then(JSON.parse) + if (pkg.private) continue + workspaces.set(pkg.name, { version: pkg.version ?? '', path }) + } + + // Build the editable output + let lines = ['# Update the versions of the packages you want to change', ''] + for (let [name, info] of workspaces) { + lines.push(`${name}: ${info.version}`) + } + let output = lines.join('\n') + + // Edit the file + { + // Figure out which editor to use. + // + // In this case we still split on whitespace, because it can happen that the + // EDITOR env variable is configured as `code --wait`. This means that we + // want `code` as the editor, but `--wait` is one of the arguments. + let args = process.env.EDITOR.split(' ') + let editor = args.shift() + + // Create a temporary file which will be edited + let filepath = path.resolve(tmpdir(), `version-${randomUUID()}.txt`) + await fs.writeFile(filepath, output) + + // Edit the file, once the editor is closed, the file will be saved and we + // can read the changes + spawnSync(editor, [...args, filepath], { + stdio: 'inherit', + }) + + let newOutput = await fs.readFile(filepath, 'utf8').then((x) => x.trim().split('\n')) + + // Cleanup temporary file + await fs.unlink(filepath) + + // Update the package.json files + for (let line of newOutput) { + if (line[0] === '#') continue // Skip comment lines + if (line.trim() === '') continue // Skip empty lines + + let [name, version = ''] = line.split(':').map((x) => x.trim()) + + // Figure out all the paths to the package.json files that need to be + // updated with the new version + let paths = [ + // The package.json file of the main workspace + workspaces.get(name).path, + + // The package.json files of the workspaces that are in sync with the + // main workspace + ...(syncedWorkspaces.get(name) ?? []), + ] + + for (let pkgPath of paths) { + let pkg = await fs.readFile(pkgPath, 'utf8').then(JSON.parse) + let name = pkg.name + if (version !== '') { + // Ensure the version is set after the name and before everything else + delete pkg.name + delete pkg.version + + // This allows us to keep the order of the keys in the package.json + pkg = { name, version, ...pkg } + } + + await fs.writeFile( + pkgPath, + await prettier + .format(JSON.stringify(pkg, null, 2), { filepath: pkgPath }) + .then((x) => `${x.trim()}\n`), + ) + } + } + } + + console.log('Done.') + }, +) diff --git a/turbo.json b/turbo.json index 35a3e63a7..c48410b29 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://turbo.build/schema.json", + "$schema": "https://v2-10-8.turborepo.dev/schema.json", "ui": "tui", "tasks": { "@tailwindcss/oxide#build": { @@ -10,6 +10,7 @@ "./build.rs", "./package.json", "./Cargo.toml", + "./.cargo/config.toml", "../oxide/src/**/*", "../oxide/Cargo.toml", "../Cargo.toml", @@ -24,6 +25,7 @@ "./build.rs", "./package.json", "./Cargo.toml", + "./.cargo/config.toml", "../oxide/src/**/*", "../oxide/Cargo.toml", "../Cargo.toml", @@ -34,7 +36,8 @@ }, "build": { "dependsOn": ["^build"], - "outputs": ["dist/**"] + "outputs": ["dist/**"], + "env": ["FEATURES_ENV"] }, "lint": {}, "dev": { diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 000000000..3ef999be2 --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: ['./packages/*', '!./packages/tsconfig.base.json'], + exclude: ['**/*.spec.?(c|m)[jt]s?(x)', 'integrations/**/*'], + }, +}) diff --git a/vitest.workspace.ts b/vitest.workspace.ts deleted file mode 100644 index 4ccde0667..000000000 --- a/vitest.workspace.ts +++ /dev/null @@ -1 +0,0 @@ -export default ['packages/*']