What happened?
Related (already fixed for SCSS/SASS, not for CSS): issue #245 — SCSS @use 'X' resolves to X.tsx instead of X.scss when both siblings exist — was fixed in commit a0944ea and shipped in v2.60.0. That fix touches the stylesheet resolver but only for .scss / .sass importers; the equivalent path for plain .css importers is unchanged. The reproduction below was re-run against v2.60.0 and the bug still reproduces.
When a .css file does a bare-package @import such as
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css';
— which Tailwind CSS v4, npm-published CSS bundles (e.g. leaflet/dist/leaflet.css, cropperjs/dist/cropper.css, highlight.js/styles/github.css), and every modern CSS bundler resolve via the standard package-name lookup in node_modules/<pkg>/<file>.css — fallow reports them in unresolved_imports as if the source had written a relative path:
{
"path": "src/main.css",
"specifier": "./tailwindcss/theme.css",
"line": 1
}
Notice the leading ./ that the source code does not contain. The original specifier in the file is tailwindcss/theme.css (a bare-package import); fallow is rewriting it to a relative path before attempting resolution and then complaining the relative file doesn't exist on disk.
This pattern is the default style for Tailwind v4 (PostCSS approach) and is widely used across npm-published CSS bundles.
Reproduction
A self-contained, copy-paste-runnable repro that produces the bug from a clean directory:
-
Create a fresh project:
mkdir repro-tailwind-css-import && cd repro-tailwind-css-import
-
Add a minimal package.json that installs Tailwind v4:
{
"name": "repro-tailwind-css-import",
"private": true,
"dependencies": {
"tailwindcss": "4.1.13"
}
}
-
Add a stub tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true
}
}
-
Create the entry CSS that uses the v4 bare-package imports:
mkdir src
cat > src/main.css <<'EOF'
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css';
EOF
-
Add a stub TS entry that side-effect-imports the CSS so fallow follows it:
cat > src/main.ts <<'EOF'
import './main.css';
export const VERSION = '1.0.0';
EOF
-
Add a fallow config:
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": ["src/main.ts"]
}
-
Install (no need to actually run Tailwind):
npm install --ignore-scripts
-
Verify Tailwind ships the imported files (sanity check that the bare-package lookup should succeed):
ls node_modules/tailwindcss/theme.css node_modules/tailwindcss/utilities.css
-
Run fallow and observe:
fallow dead-code --format json --quiet | jq '.unresolved_imports'
Expected behavior
Fallow should resolve @import 'tailwindcss/theme.css' the same way every CSS bundler does:
- Try
node_modules/tailwindcss/theme.css (exists ✅) — done.
- If that misses, fall back to the package's
exports / style field.
- Only if both miss, report unresolved — and report the original specifier (
tailwindcss/theme.css), not a rewritten relative form.
In this repro: no entry in unresolved_imports, and tailwindcss is correctly counted as used.
Fallow version
2.60.0
Operating system
macOS
Configuration
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": ["src/main.ts"]
}
What happened?
When a
.cssfile does a bare-package@importsuch as— which Tailwind CSS v4, npm-published CSS bundles (e.g.
leaflet/dist/leaflet.css,cropperjs/dist/cropper.css,highlight.js/styles/github.css), and every modern CSS bundler resolve via the standard package-name lookup innode_modules/<pkg>/<file>.css— fallow reports them inunresolved_importsas if the source had written a relative path:{ "path": "src/main.css", "specifier": "./tailwindcss/theme.css", "line": 1 }Notice the leading
./that the source code does not contain. The original specifier in the file istailwindcss/theme.css(a bare-package import); fallow is rewriting it to a relative path before attempting resolution and then complaining the relative file doesn't exist on disk.This pattern is the default style for Tailwind v4 (PostCSS approach) and is widely used across npm-published CSS bundles.
Reproduction
A self-contained, copy-paste-runnable repro that produces the bug from a clean directory:
Create a fresh project:
Add a minimal
package.jsonthat installs Tailwind v4:{ "name": "repro-tailwind-css-import", "private": true, "dependencies": { "tailwindcss": "4.1.13" } }Add a stub
tsconfig.json:{ "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "strict": true, "skipLibCheck": true } }Create the entry CSS that uses the v4 bare-package imports:
Add a stub TS entry that side-effect-imports the CSS so fallow follows it:
Add a fallow config:
{ "$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json", "entry": ["src/main.ts"] }Install (no need to actually run Tailwind):
Verify Tailwind ships the imported files (sanity check that the bare-package lookup should succeed):
Run fallow and observe:
Expected behavior
Fallow should resolve
@import 'tailwindcss/theme.css'the same way every CSS bundler does:node_modules/tailwindcss/theme.css(exists ✅) — done.exports/stylefield.tailwindcss/theme.css), not a rewritten relative form.In this repro: no entry in
unresolved_imports, andtailwindcssis correctly counted as used.Fallow version
2.60.0
Operating system
macOS
Configuration