From 4363426384d98c3545491738b26afd6a0c03e22c Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 6 Nov 2025 16:24:17 -0500 Subject: [PATCH 1/3] =?UTF-8?q?Fix=20validation=20of=20`source(=E2=80=A6)`?= =?UTF-8?q?=20paths=20(#19274)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #18833 - [x] Needs tests Basically we were correctly resolving the path given to `source()` inside Oxide *but* inside `@tailwindcss/node` when we validated that the path was a directory we were not. We incorrectly used the base path of the input file rather than the file the `source(…)` directive was defined in. This PR fixes that. --- CHANGELOG.md | 4 ++ integrations/cli/index.test.ts | 59 +++++++++++++++++ integrations/postcss/index.test.ts | 70 ++++++++++++++++++++ integrations/vite/index.test.ts | 80 +++++++++++++++++++++++ packages/@tailwindcss-node/src/compile.ts | 17 ++--- 5 files changed, 222 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3b0b97718c..682b66628eb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Ensure validation of `source(…)` happens relative to the file it is in ([#19274](https://github.com/tailwindlabs/tailwindcss/pull/19274)) + ### Added - _Experimental_: Add `@container-size` utility ([#18901](https://github.com/tailwindlabs/tailwindcss/pull/18901)) diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index be48a2ac4898..9b666f4ec313 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -1332,6 +1332,65 @@ test( }, ) +test( + 'source(…) and `@source` are relative to the file they are in', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + '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 tailwindcss --input ./index.css --output dist/out.css', { cwd: root }) + + let content = await fs.dumpFiles('./dist/*.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( 'auto source detection disabled', { diff --git a/integrations/postcss/index.test.ts b/integrations/postcss/index.test.ts index dd07fd90ebbd..08ea852f950c 100644 --- a/integrations/postcss/index.test.ts +++ b/integrations/postcss/index.test.ts @@ -825,3 +825,73 @@ test( }) }, ) + +test( + 'source(…) and `@source` are relative to the file they are in', + { + fs: { + 'package.json': json` + { + "dependencies": { + "postcss": "^8", + "postcss-cli": "^10", + "tailwindcss": "workspace:^", + "@tailwindcss/postcss": "workspace:^" + } + } + `, + + 'postcss.config.js': js` + module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + }, + } + `, + + '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, root, expect }) => { + await exec('pnpm postcss ./index.css --output dist/out.css', { cwd: root }) + + let content = await fs.dumpFiles('./dist/*.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']`) + }, +) diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts index ead71b47296f..91570a6fcc2d 100644 --- a/integrations/vite/index.test.ts +++ b/integrations/vite/index.test.ts @@ -730,6 +730,86 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { 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( diff --git a/packages/@tailwindcss-node/src/compile.ts b/packages/@tailwindcss-node/src/compile.ts index 5ce6299af6a2..ac7cc6f15211 100644 --- a/packages/@tailwindcss-node/src/compile.ts +++ b/packages/@tailwindcss-node/src/compile.ts @@ -63,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 = /[*{]/ @@ -80,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 } From 5a89571efda7a3218ece005051028d3c3eb02cb5 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Sun, 9 Nov 2025 07:31:49 -0500 Subject: [PATCH 2/3] =?UTF-8?q?Update=20eslint=209.37.0=20=E2=86=92=209.39?= =?UTF-8?q?.1=20(minor)=20(#19287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here is everything you need to know about this update. Please take a good look at what changed and the test results before merging this pull request. ### What changed? #### ✳️ eslint (9.37.0 → 9.39.1) · [Repo](https://github.com/eslint/eslint) · [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
Release Notes

9.39.1

Bug Fixes

  • 650753e fix: Only pass node to JS lang visitor methods (#20283) (Nicholas C. Zakas)

Documentation

  • 51b51f4 docs: add a section on when to use extends vs cascading (#20268) (Tanuj Kanti)
  • b44d426 docs: Update README (GitHub Actions Bot)

Chores

  • 92db329 chore: update @eslint/js version to 9.39.1 (#20284) (Francesco Trotta)
  • c7ebefc chore: package.json update for @eslint/js release (Jenkins)
  • 61778f6 chore: update eslint-config-eslint dependency @eslint/js to ^9.39.0 (#20275) (renovate[bot])
  • d9ca2fc ci: Add rangeStrategy to eslint group in renovate config (#20266) (唯然)
  • 009e507 test: fix version tests for ESLint v10 (#20274) (Milos Djermanovic)

9.39.0

Features

  • cc57d87 feat: update error loc to key in no-dupe-class-members (#20259) (Tanuj Kanti)
  • 126552f feat: update error location in for-direction and no-dupe-args (#20258) (Tanuj Kanti)
  • 167d097 feat: update complexity rule to highlight only static block header (#20245) (jaymarvelz)

Bug Fixes

  • 15f5c7c fix: forward traversal step.args to visitors (#20253) (jaymarvelz)
  • 5a1a534 fix: allow JSDoc comments in object-shorthand rule (#20167) (Nitin Kumar)
  • e86b813 fix: Use more types from @eslint/core (#20257) (Nicholas C. Zakas)
  • 927272d fix: correct Scope typings (#20198) (jaymarvelz)
  • 37f76d9 fix: use AST.Program type for Program node (#20244) (Francesco Trotta)
  • ae07f0b fix: unify timing report for concurrent linting (#20188) (jaymarvelz)
  • b165d47 fix: correct Rule typings (#20199) (jaymarvelz)
  • fb97cda fix: improve error message for missing fix function in suggestions (#20218) (jaymarvelz)

Documentation

  • d3e81e3 docs: Always recommend to include a files property (#20158) (Percy Ma)
  • 0f0385f docs: use consistent naming recommendation (#20250) (Alex M. Spieslechner)
  • a3b1456 docs: Update README (GitHub Actions Bot)
  • cf5f2dd docs: fix correct tag of no-useless-constructor (#20255) (Tanuj Kanti)
  • 10b995c docs: add TS options and examples for nofunc in no-use-before-define (#20249) (Tanuj Kanti)
  • 2584187 docs: remove repetitive word in comment (#20242) (reddaisyy)
  • 637216b docs: update CLI flags migration instructions (#20238) (jaymarvelz)
  • e7cda3b docs: Update README (GitHub Actions Bot)
  • 7b9446f docs: handle empty flags sections on the feature flags page (#20222) (sethamus)

Chores

  • dfe3c1b chore: update @eslint/js version to 9.39.0 (#20270) (Francesco Trotta)
  • 2375a6d chore: package.json update for @eslint/js release (Jenkins)
  • a1f4e52 chore: update @eslint dependencies (#20265) (Francesco Trotta)
  • c7d3229 chore: update dependency @eslint/core to ^0.17.0 (#20256) (renovate[bot])
  • 27549bc chore: update fuzz testing to not error if code sample minimizer fails (#20252) (Milos Djermanovic)
  • a1370ee ci: bump actions/setup-node from 5 to 6 (#20230) (dependabot[bot])
  • 9e7fad4 chore: add script to auto-generate eslint:recommended configuration (#20208) (唯然)

9.38.0

Features

  • ce40f74 feat: update complexity rule to only highlight function header (#20048) (Atul Nair)
  • e37e590 feat: correct no-loss-of-precision false positives with e notation (#20187) (Francesco Trotta)

Bug Fixes

  • 50c3dfd fix: improve type support for isolated dependencies in pnpm (#20201) (Francesco Trotta)
  • a1f06a3 fix: correct SourceCode typings (#20114) (Pixel998)

Documentation

  • 462675a docs: improve web accessibility by hiding non-semantic character (#20205) (루밀LuMir)
  • c070e65 docs: correct formatting in no-irregular-whitespace rule documentation (#20203) (루밀LuMir)
  • b39e71a docs: Update README (GitHub Actions Bot)
  • cd39983 docs: move custom-formatters type descriptions to nodejs-api (#20190) (Percy Ma)

Chores

  • d17c795 chore: upgrade @eslint/js@9.38.0 (#20221) (Milos Djermanovic)
  • 25d0e33 chore: package.json update for @eslint/js release (Jenkins)
  • c82b5ef refactor: Use types from @eslint/core (#20168) (Nicholas C. Zakas)
  • ff31609 ci: add Node.js 25 to ci.yml (#20220) (루밀LuMir)
  • 004577e ci: bump github/codeql-action from 3 to 4 (#20211) (dependabot[bot])
  • eac71fb test: remove use of nodejsScope option of eslint-scope from tests (#20206) (Milos Djermanovic)
  • 4168a18 chore: fix typo in legacy-eslint.js (#20202) (Sweta Tanwar)
  • 205dbd2 chore: fix typos (#20200) (ntnyq)
  • dbb200e chore: use team member's username when name is not available in data (#20194) (Milos Djermanovic)
  • 8962089 chore: mark deprecated rules as available until v11.0.0 (#20184) (Pixel998)

Does any of this look wrong? Please let us know.

Commits

See the full diff on Github. The new version differs by 59 commits:

--- ![Depfu Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg) [Depfu](https://depfu.com) will automatically keep this PR conflict-free, as long as you don't add any commits to this branch yourself. You can also trigger a rebase manually by commenting with `@depfu rebase`.
All Depfu comment commands
@​depfu rebase
Rebases against your default branch and redoes this update
@​depfu recreate
Recreates this PR, overwriting any edits that you've made to it
@​depfu merge
Merges this PR once your tests are passing and conflicts are resolved
@​depfu cancel merge
Cancels automatic merging of this PR
@​depfu close
Closes this PR and deletes the branch
@​depfu reopen
Restores the branch and reopens this PR (if it's closed)
@​depfu pause
Ignores all future updates for this dependency and closes this PR
@​depfu pause [minor|major]
Ignores all future minor/major updates for this dependency and closes this PR
@​depfu resume
Future versions of this dependency will create PRs again (leaves this PR as is)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> --- playgrounds/nextjs/package.json | 2 +- playgrounds/v3/package.json | 2 +- pnpm-lock.yaml | 221 ++++++++++++++++---------------- 3 files changed, 112 insertions(+), 113 deletions(-) diff --git a/playgrounds/nextjs/package.json b/playgrounds/nextjs/package.json index a822b0dd9745..af85e1c5de5d 100644 --- a/playgrounds/nextjs/package.json +++ b/playgrounds/nextjs/package.json @@ -20,7 +20,7 @@ "@types/node": "catalog:", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", - "eslint": "^9.37.0", + "eslint": "^9.39.1", "eslint-config-next": "^15.5.6", "typescript": "^5.5.4" } diff --git a/playgrounds/v3/package.json b/playgrounds/v3/package.json index 121f31e52abc..424125031ea5 100644 --- a/playgrounds/v3/package.json +++ b/playgrounds/v3/package.json @@ -19,7 +19,7 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "autoprefixer": "^10.4.21", - "eslint": "^9.37.0", + "eslint": "^9.39.1", "eslint-config-next": "^15.5.6", "typescript": "^5.5.4" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23f77508367f..d4c95dcdcdcc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -502,11 +502,11 @@ importers: specifier: ^19.2.2 version: 19.2.2(@types/react@19.2.2) eslint: - specifier: ^9.37.0 - version: 9.37.0(jiti@2.6.1) + specifier: ^9.39.1 + version: 9.39.1(jiti@2.6.1) eslint-config-next: specifier: ^15.5.6 - version: 15.5.6(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) + version: 15.5.6(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -539,11 +539,11 @@ importers: specifier: ^10.4.21 version: 10.4.21(postcss@8.4.47) eslint: - specifier: ^9.37.0 - version: 9.37.0(jiti@2.6.1) + specifier: ^9.39.1 + version: 9.39.1(jiti@2.6.1) eslint-config-next: specifier: ^15.5.6 - version: 15.5.6(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) + version: 15.5.6(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) typescript: specifier: ^5.5.4 version: 5.6.3 @@ -1129,32 +1129,32 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.4.0': - resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.16.0': - resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.37.0': - resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.4.0': - resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': @@ -3067,8 +3067,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.37.0: - resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -5019,26 +5019,26 @@ snapshots: '@esbuild/win32-x64@0.25.0': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 + '@eslint/object-schema': 2.1.7 debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.4.0': + '@eslint/config-helpers@0.4.2': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 - '@eslint/core@0.16.0': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 @@ -5056,13 +5056,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.37.0': {} + '@eslint/js@9.39.1': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.4.0': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 levn: 0.4.1 '@humanfs/core@0.19.1': {} @@ -6058,15 +6058,15 @@ snapshots: '@types/semver@7.7.1': {} - '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/type-utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) - '@typescript-eslint/utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.11.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -6076,15 +6076,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) '@typescript-eslint/scope-manager': 8.11.0 - '@typescript-eslint/type-utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) - '@typescript-eslint/utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/type-utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) '@typescript-eslint/visitor-keys': 8.11.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -6094,27 +6094,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4)': + '@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.4.3 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3)': + '@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.4.3 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: @@ -6125,10 +6125,10 @@ snapshots: '@typescript-eslint/types': 8.11.0 '@typescript-eslint/visitor-keys': 8.11.0 - '@typescript-eslint/type-utils@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) debug: 4.4.3 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: @@ -6137,10 +6137,10 @@ snapshots: - eslint - supports-color - '@typescript-eslint/type-utils@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3)': dependencies: '@typescript-eslint/typescript-estree': 8.11.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/utils': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) debug: 4.4.3 ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: @@ -6181,24 +6181,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4)': + '@typescript-eslint/utils@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@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.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3)': + '@typescript-eslint/utils@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@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.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript @@ -6820,19 +6820,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@15.5.6(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4): + eslint-config-next@15.5.6(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4): dependencies: '@next/eslint-plugin-next': 15.5.6 '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) - eslint: 9.37.0(jiti@2.6.1) + '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.1(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-react: 7.37.2(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-react-hooks: 5.0.0(eslint@9.37.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react: 7.37.2(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-hooks: 5.0.0(eslint@9.39.1(jiti@2.6.1)) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -6840,19 +6840,19 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-next@15.5.6(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3): + eslint-config-next@15.5.6(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3): dependencies: '@next/eslint-plugin-next': 15.5.6 '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) - eslint: 9.37.0(jiti@2.6.1) + '@typescript-eslint/eslint-plugin': 8.11.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.1(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-react: 7.37.2(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-react-hooks: 5.0.0(eslint@9.37.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react: 7.37.2(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-hooks: 5.0.0(eslint@9.39.1(jiti@2.6.1)) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: @@ -6868,67 +6868,67 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 enhanced-resolve: 5.18.3 - eslint: 9.37.0(jiti@2.6.1) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)) + eslint: 9.39.1(jiti@2.6.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) fast-glob: 3.3.3 get-tsconfig: 4.10.0 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.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint@9.37.0(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint@9.39.1(jiti@2.6.1)) 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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 enhanced-resolve: 5.18.3 - eslint: 9.37.0(jiti@2.6.1) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)) + eslint: 9.39.1(jiti@2.6.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) fast-glob: 3.3.3 get-tsconfig: 4.10.0 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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.37.0(jiti@2.6.1)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.39.1(jiti@2.6.1)) 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.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) - eslint: 9.37.0(jiti@2.6.1) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) - eslint: 9.37.0(jiti@2.6.1) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -6937,9 +6937,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -6951,13 +6951,13 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.5.4) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -6966,9 +6966,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.37.0(jiti@2.6.1))(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.37.0(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.39.1(jiti@2.6.1))(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.39.1(jiti@2.6.1))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -6980,13 +6980,13 @@ snapshots: string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.11.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.6.3) + '@typescript-eslint/parser': 8.11.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.6.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.1(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-jsx-a11y@6.10.1(eslint@9.39.1(jiti@2.6.1)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -6997,7 +6997,7 @@ snapshots: damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 es-iterator-helpers: 1.1.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -7006,11 +7006,11 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.0.0(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-react-hooks@5.0.0(eslint@9.39.1(jiti@2.6.1)): dependencies: - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-react@7.37.2(eslint@9.37.0(jiti@2.6.1)): + eslint-plugin-react@7.37.2(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -7018,7 +7018,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.1.0 - eslint: 9.37.0(jiti@2.6.1) + eslint: 9.39.1(jiti@2.6.1) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -7041,21 +7041,20 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.37.0(jiti@2.6.1): + eslint@9.39.1(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.4.0 - '@eslint/core': 0.16.0 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.37.0 - '@eslint/plugin-kit': 0.4.0 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 From 5bc90dd2e0e009a6ccc0870367708ca724af23dc Mon Sep 17 00:00:00 2001 From: Ishita SIngh Date: Mon, 10 Nov 2025 23:59:23 +0530 Subject: [PATCH 3/3] Include filename and line numbers in CSS parse errors (#19282) Co-authored-by: Jordan Pittman --- CHANGELOG.md | 1 + integrations/cli/index.test.ts | 30 +++++++ packages/tailwindcss/src/css-parser.test.ts | 97 +++++++++++++++++++-- packages/tailwindcss/src/css-parser.ts | 78 ++++++++++++++--- 4 files changed, 185 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 682b66628eb2..e44a8358a064 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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)) ### Added diff --git a/integrations/cli/index.test.ts b/integrations/cli/index.test.ts index 9b666f4ec313..5f3b0fcb42ca 100644 --- a/integrations/cli/index.test.ts +++ b/integrations/cli/index.test.ts @@ -1,9 +1,12 @@ import dedent from 'dedent' import os from 'node:os' import path from 'node:path' +import { fileURLToPath } from 'node:url' import { describe } from 'vitest' import { candidate, css, html, js, json, test, ts, yaml } from '../utils' +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + const STANDALONE_BINARY = (() => { switch (os.platform()) { case 'win32': @@ -2101,6 +2104,33 @@ test( }, ) +test( + 'CSS parse errors should include filename and line number', + { + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "workspace:^", + "@tailwindcss/cli": "workspace:^" + } + } + `, + 'input.css': css` + .test { + color: red; + */ + } + `, + }, + }, + async ({ exec, expect }) => { + await expect(exec('pnpm tailwindcss --input input.css --output dist/out.css')).rejects.toThrow( + /CssSyntaxError: .*input.css:3:3: Invalid declaration: `\*\/`/, + ) + }, +) + function withBOM(text: string): string { return '\uFEFF' + text } diff --git a/packages/tailwindcss/src/css-parser.test.ts b/packages/tailwindcss/src/css-parser.test.ts index 25657d9db770..5f0aa2287ae6 100644 --- a/packages/tailwindcss/src/css-parser.test.ts +++ b/packages/tailwindcss/src/css-parser.test.ts @@ -8,6 +8,12 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { return CSS.parse(string.replaceAll(/\r?\n/g, lineEndings === 'Windows' ? '\r\n' : '\n')) } + function parseWithLoc(string: string) { + return CSS.parse(string.replaceAll(/\r?\n/g, lineEndings === 'Windows' ? '\r\n' : '\n'), { + from: 'input.css', + }) + } + describe('comments', () => { it('should parse a comment and ignore it', () => { expect( @@ -1145,7 +1151,20 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { color: blue; } `), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Missing opening {]`) + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: Missing opening {]`) + + expect(() => + parseWithLoc(` + .foo { + color: red; + } + + .bar + /* ^ Missing opening { */ + color: blue; + } + `), + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: input.css:9:11: Missing opening {]`) }) it('should error when curly brackets are unbalanced (closing)', () => { @@ -1160,7 +1179,22 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { /* ^ Missing closing } */ `), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Missing closing } at .bar]`) + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: Missing closing } at .bar]`) + + expect(() => + parseWithLoc(` + .foo { + color: red; + } + + .bar { + color: blue; + + /* ^ Missing closing } */ + `), + ).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:6:11: Missing closing } at .bar]`, + ) }) it('should error when an unterminated string is used', () => { @@ -1172,7 +1206,19 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { font-weight: bold; } `), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Unterminated string: "Hello world!"]`) + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: Unterminated string: "Hello world!"]`) + + expect(() => + parseWithLoc(css` + .foo { + content: "Hello world! + /* ^ missing " */ + font-weight: bold; + } + `), + ).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:3:22: Unterminated string: "Hello world!"]`, + ) }) it('should error when an unterminated string is used with a `;`', () => { @@ -1184,18 +1230,38 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { font-weight: bold; } `), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Unterminated string: "Hello world!;"]`) + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: Unterminated string: "Hello world!;"]`) + + expect(() => + parseWithLoc(css` + .foo { + content: "Hello world!; + /* ^ missing " */ + font-weight: bold; + } + `), + ).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:3:22: Unterminated string: "Hello world!;"]`, + ) }) it('should error when incomplete custom properties are used', () => { expect(() => parse('--foo')).toThrowErrorMatchingInlineSnapshot( - `[Error: Invalid custom property, expected a value]`, + `[CssSyntaxError: Invalid custom property, expected a value]`, + ) + + expect(() => parseWithLoc('--foo')).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:1:1: Invalid custom property, expected a value]`, ) }) it('should error when incomplete custom properties are used inside rules', () => { expect(() => parse('.foo { --bar }')).toThrowErrorMatchingInlineSnapshot( - `[Error: Invalid custom property, expected a value]`, + `[CssSyntaxError: Invalid custom property, expected a value]`, + ) + + expect(() => parseWithLoc('.foo { --bar }')).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:1:8: Invalid custom property, expected a value]`, ) }) @@ -1207,12 +1273,27 @@ describe.each(['Unix', 'Windows'])('Line endings: %s', (lineEndings) => { /* ^ missing ' * /; } `), - ).toThrowErrorMatchingInlineSnapshot(`[Error: Unterminated string: 'Hello world!']`) + ).toThrowErrorMatchingInlineSnapshot(`[CssSyntaxError: Unterminated string: 'Hello world!']`) + + expect(() => + parseWithLoc(css` + .foo { + --bar: 'Hello world! + /* ^ missing ' * /; + } + `), + ).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:3:20: Unterminated string: 'Hello world!']`, + ) }) it('should error when a declaration is incomplete', () => { expect(() => parse('.foo { bar }')).toThrowErrorMatchingInlineSnapshot( - `[Error: Invalid declaration: \`bar\`]`, + `[CssSyntaxError: Invalid declaration: \`bar\`]`, + ) + + expect(() => parseWithLoc('.foo { bar }')).toThrowErrorMatchingInlineSnapshot( + `[CssSyntaxError: input.css:1:8: Invalid declaration: \`bar\`]`, ) }) }) diff --git a/packages/tailwindcss/src/css-parser.ts b/packages/tailwindcss/src/css-parser.ts index 1653c3789b28..3dd5e6ffe6cf 100644 --- a/packages/tailwindcss/src/css-parser.ts +++ b/packages/tailwindcss/src/css-parser.ts @@ -9,7 +9,8 @@ import { type Declaration, type Rule, } from './ast' -import type { Source } from './source-maps/source' +import { createLineTable } from './source-maps/line-table' +import type { Source, SourceLocation } from './source-maps/source' const BACKSLASH = 0x5c const SLASH = 0x2f @@ -36,6 +37,30 @@ export interface ParseOptions { from?: string } +/** + * CSS syntax error with source location information. + */ +export class CssSyntaxError extends Error { + loc: SourceLocation | null + + constructor(message: string, loc: SourceLocation | null) { + if (loc) { + let source = loc[0] + let start = createLineTable(source.code).find(loc[1]) + message = `${source.file}:${start.line}:${start.column + 1}: ${message}` + } + + super(message) + + this.name = 'CssSyntaxError' + this.loc = loc + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, CssSyntaxError) + } + } +} + export function parse(input: string, opts?: ParseOptions) { let source: Source | null = opts?.from ? { file: opts.from, code: input } : null @@ -138,7 +163,7 @@ export function parse(input: string, opts?: ParseOptions) { // Start of a string. else if (currentChar === SINGLE_QUOTE || currentChar === DOUBLE_QUOTE) { - let end = parseString(input, i, currentChar) + let end = parseString(input, i, currentChar, source) // Adjust `buffer` to include the string. buffer += input.slice(i, end + 1) @@ -192,7 +217,7 @@ export function parse(input: string, opts?: ParseOptions) { // Start of a string. else if (peekChar === SINGLE_QUOTE || peekChar === DOUBLE_QUOTE) { - j = parseString(input, j, peekChar) + j = parseString(input, j, peekChar, source) } // Start of a comment. @@ -269,7 +294,12 @@ export function parse(input: string, opts?: ParseOptions) { } let declaration = parseDeclaration(buffer, colonIdx) - if (!declaration) throw new Error(`Invalid custom property, expected a value`) + if (!declaration) { + throw new CssSyntaxError( + `Invalid custom property, expected a value`, + source ? [source, start, i] : null, + ) + } if (source) { declaration.src = [source, start, i] @@ -334,7 +364,10 @@ export function parse(input: string, opts?: ParseOptions) { let declaration = parseDeclaration(buffer) if (!declaration) { if (buffer.length === 0) continue - throw new Error(`Invalid declaration: \`${buffer.trim()}\``) + throw new CssSyntaxError( + `Invalid declaration: \`${buffer.trim()}\``, + source ? [source, bufferStart, i] : null, + ) } if (source) { @@ -391,7 +424,7 @@ export function parse(input: string, opts?: ParseOptions) { closingBracketStack[closingBracketStack.length - 1] !== ')' ) { if (closingBracketStack === '') { - throw new Error('Missing opening {') + throw new CssSyntaxError('Missing opening {', source ? [source, i, i] : null) } closingBracketStack = closingBracketStack.slice(0, -1) @@ -453,7 +486,12 @@ export function parse(input: string, opts?: ParseOptions) { // Attach the declaration to the parent. if (parent) { let node = parseDeclaration(buffer, colonIdx) - if (!node) throw new Error(`Invalid declaration: \`${buffer.trim()}\``) + if (!node) { + throw new CssSyntaxError( + `Invalid declaration: \`${buffer.trim()}\``, + source ? [source, bufferStart, i] : null, + ) + } if (source) { node.src = [source, bufferStart, i] @@ -492,7 +530,7 @@ export function parse(input: string, opts?: ParseOptions) { // `)` else if (currentChar === CLOSE_PAREN) { if (closingBracketStack[closingBracketStack.length - 1] !== ')') { - throw new Error('Missing opening (') + throw new CssSyntaxError('Missing opening (', source ? [source, i, i] : null) } closingBracketStack = closingBracketStack.slice(0, -1) @@ -534,10 +572,17 @@ export function parse(input: string, opts?: ParseOptions) { // have a leftover `parent`, then it means that we have an unterminated block. if (closingBracketStack.length > 0 && parent) { if (parent.kind === 'rule') { - throw new Error(`Missing closing } at ${parent.selector}`) + throw new CssSyntaxError( + `Missing closing } at ${parent.selector}`, + parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null, + ) } + if (parent.kind === 'at-rule') { - throw new Error(`Missing closing } at ${parent.name} ${parent.params}`) + throw new CssSyntaxError( + `Missing closing } at ${parent.name} ${parent.params}`, + parent.src ? [parent.src[0], parent.src[1], parent.src[1]] : null, + ) } } @@ -594,7 +639,12 @@ function parseDeclaration( ) } -function parseString(input: string, startIdx: number, quoteChar: number): number { +function parseString( + input: string, + startIdx: number, + quoteChar: number, + source: Source | null = null, +): number { let peekChar: number // We need to ensure that the closing quote is the same as the opening @@ -636,8 +686,9 @@ function parseString(input: string, startIdx: number, quoteChar: number): number (input.charCodeAt(i + 1) === LINE_BREAK || (input.charCodeAt(i + 1) === CARRIAGE_RETURN && input.charCodeAt(i + 2) === LINE_BREAK)) ) { - throw new Error( + throw new CssSyntaxError( `Unterminated string: ${input.slice(startIdx, i + 1) + String.fromCharCode(quoteChar)}`, + source ? [source, startIdx, i + 1] : null, ) } @@ -655,8 +706,9 @@ function parseString(input: string, startIdx: number, quoteChar: number): number peekChar === LINE_BREAK || (peekChar === CARRIAGE_RETURN && input.charCodeAt(i + 1) === LINE_BREAK) ) { - throw new Error( + throw new CssSyntaxError( `Unterminated string: ${input.slice(startIdx, i) + String.fromCharCode(quoteChar)}`, + source ? [source, startIdx, i + 1] : null, ) } }