Skip to content

@tailwindcss/vite 4.3.1 breaks Vite tsconfig extends resolution under Deno #20232

Description

@neunzehn86

Summary

@tailwindcss/vite@4.3.1 breaks Vite's tsconfig extends resolution when running under Deno with nodeModulesDir: "auto" and npm imports from deno.json.

The same reproduction repository:

  • fails with deno task build
  • passes with Node via ./node_modules/.bin/vite build
  • passes under Deno when Tailwind is pinned back to 4.3.0

Reproduction

Reproduction repository:

https://github.com/neunzehn86/tailwind-deno-registerhooks-repro.git

Steps:

git clone https://github.com/neunzehn86/tailwind-deno-registerhooks-repro.git
cd tailwind-deno-registerhooks-repro
deno task build

Actual Result

error during build:
[vite:build-html] failed to resolve "extends":"@vue/tsconfig/tsconfig.dom.json" in .../tsconfig.app.json
file: .../_dev/index.html
    at resolveExtends (.../node_modules/.deno/vite@7.3.5/node_modules/vite/dist/node/chunks/config.js:5809:8)
    at parseExtends (.../node_modules/.deno/vite@7.3.5/node_modules/vite/dist/node/chunks/config.js:5775:71)
    ...

In the original project, Vitest showed the nested cause more clearly:

Caused by: TypeError: Invalid URL: '/.../tsconfig.app.json'
 ❯ new URL ../ext:deno_web/00_url.js
 ❯ Pt ../node_modules/.deno/@tailwindcss+node@4.3.1/node_modules/@tailwindcss/node/dist/index.mjs
 ❯ De ../node_modules/.deno/@tailwindcss+node@4.3.1/node_modules/@tailwindcss/node/dist/index.mjs
 ❯ resolveExtends ../node_modules/.deno/vite@7.3.5/node_modules/vite/dist/node/chunks/config.js

Expected Result

Vite should resolve @vue/tsconfig/tsconfig.dom.json successfully under Deno, as it does:

  • with @tailwindcss/vite@4.3.0
  • with Node using the same Deno-generated node_modules

Environment

Observed locally:

deno 2.8.2
node v22.14.0
macOS arm64

Package versions in the reproduction:

@tailwindcss/vite 4.3.1
@tailwindcss/node 4.3.1
tailwindcss 4.3.1
vite 7.3.5
@vitejs/plugin-vue 6.0.7
@vue/tsconfig 0.8.1
typescript 5.9.3
vue 3.5.38

Node Comparison

After deno task build has generated node_modules, running Vite through Node succeeds:

./node_modules/.bin/vite build

Result:

vite v7.3.5 building client environment for production...
✓ built

Regression Boundary

Changing the reproduction to Tailwind 4.3.0 makes the Deno build pass:

"@tailwindcss/vite": "npm:@tailwindcss/vite@4.3.0",
"tailwindcss": "npm:tailwindcss@4.3.0"

This suggests the regression was introduced in 4.3.1, not the broader 4.3.x line.

Notes From Investigation

@tailwindcss/node@4.3.1 appears to introduce/use node:module.registerHooks when available:

if (!process.versions.bun) {
  if (M.registerHooks) {
    M.registerHooks({ resolve: De });
  } else {
    let e = M.createRequire(import.meta.url);
    M.register?.(on(e.resolve("@tailwindcss/node/esm-cache-loader")));
  }
}

In this local environment:

Deno 2.8.2: node:module.registerHooks exists
Node v22.14.0: node:module.registerHooks is undefined

So Tailwind takes the registerHooks path under Deno, but not under Node.

By contrast, @tailwindcss/node@4.3.0 still uses the older loader registration path:

let e = M.createRequire(import.meta.url);
M.register?.(on(e.resolve("@tailwindcss/node/esm-cache-loader")));

The registered resolve hook seems to receive a plain filesystem path as context.parentURL during Vite/tsconfck's createRequire(from).resolve(...) call. It then calls new URL(context.parentURL), which throws because context.parentURL is /.../tsconfig.app.json rather than a file://... URL.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions