Commit Graph

2 Commits

Author SHA1 Message Date
James Anderson 7d502f6de5 fix(scss): resolve tilde (~) imports from node_modules and project root (#1881)
* fix(scss): resolve tilde (~) imports from node_modules and project root (#1825)

Next.js (via sass-loader's webpackImporter) supports two tilde import
forms in SCSS: `~pkg/path` (node_modules) and `~/path` (project root).
Vite's built-in Sass resolver does not strip the `~` prefix, so any
SCSS using tilde imports fails with "Can't find stylesheet to import".

This adds `createSassTildeImporter(root)` – a Sass FileImporter
(modern API) injected first in `css.preprocessorOptions.scss.importers`
for every vinext project. It:

- Returns `null` for non-tilde URLs, passing through to Vite's resolver
- `~/path` → resolves relative to the Vite project root
- `~pkg/path` → fast-path checks `<root>/node_modules/<pkg>` directly;
  falls back to `createRequire`-based resolution for hoisted pnpm graphs,
  yarn PnP, and workspace monorepos

The importer is always injected (not just when sassOptions is set) so
test fixtures that don't configure sassOptions still get tilde support.

Fixes #1825

Affected tests:
- test/e2e/app-dir/scss/npm-import-tilde (tilde imports)
- test/e2e/app-dir/scss/nm-module (CSS module from node_modules)

* fix(scss): fix lint errors in sass-options test (unused import, unsafe optional chaining)

Remove unused `import fs from "node:fs"` and replace unsafe optional-chaining
casts (`opts?.scss?.importers as any[]`) with guarded local variables so
oxlint's no-unsafe-optional-chaining rule is satisfied. Also applies the
formatter fix already caught by `vp check --fix`.
2026-06-09 15:43:20 +01:00
James Anderson 9252ca2966 fix(css): pass sassOptions from next.config to Vite preprocessor (#1277)
* fix(css): pass sassOptions from next.config to Vite preprocessor

Next.js threads `sassOptions` from `next.config.js` into sass-loader,
destructuring `prependData` / `additionalData` / `implementation` and
forwarding the rest (e.g. `includePaths`, `loadPaths`) as Sass options.
vinext was dropping this entirely, so SCSS fixtures that rely on
`additionalData` to define variables, `includePaths`/`loadPaths` to
resolve partials, or a custom `implementation` all failed to build
with `Undefined variable` / `Can't find stylesheet to import`.

Resolve `sassOptions` from next.config into `ResolvedNextConfig`, then
translate it onto Vite's `css.preprocessorOptions.scss` (and `.sass`)
shape:

  - prependData precedence matches Next.js (prependData || additionalData)
  - legacy includePaths is merged into modern loadPaths (Vite uses
    modern Sass, which dropped includePaths)
  - implementation and any remaining Sass options are passed through

Affected upstream fixtures:
  - test/e2e/app-dir/scss/basic-module-include-paths
  - test/e2e/app-dir/scss/basic-module-additional-data
  - test/e2e/app-dir/scss/basic-module-prepend-data

Refs: cloudflare/vinext CI run 25951111875
Source: packages/next/src/build/webpack/config/blocks/css/index.ts

* fix(css): address sass passthrough review feedback

- Drop `export` on `VitePreprocessorOptions` so Knip stops flagging it
  as unused (CI Check blocker).
- Use truthy-OR (`prependData || additionalData`) to match Next.js's
  `sassPrependData || sassAdditionalData` exactly — empty-string
  `prependData` now falls through to `additionalData` instead of
  overriding it.
- Widen `additionalData` type to accept Vite's function form
  (`(source, filename) => string | Promise<string>`) and drop the
  inaccurate `as string` cast.
- Add tests covering the empty-`prependData` fallthrough and the
  function-form `additionalData` passthrough.
2026-05-16 19:45:03 +01:00