Добавлен sass-importer

This commit is contained in:
2026-09-08 13:20:29 +04:00
parent 8c7e9aa5e0
commit 12c14e8eb5
3 changed files with 234 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# @advdominion/sass-importer
Кастомный импортёр для [sass-embedded](https://github.com/sass/dart-sass-embedded), который берёт резолв `@use`/`@forward`-импортов на себя и читает файлы через `node:fs`.
## Зачем
В проектах на **Yarn PnP** пакеты лежат не в `node_modules`, а внутри `.zip` по виртуальным путям:
```
.yarn/__virtual__/@example-example-virtual-.../cache/@example-example-....zip/node_modules/@example/example/...
```
`.zip`-путь может открыть только код, читающий файлы через `node:fs` (в нём есть PnP-хуки). А `sass-embedded` читает файлы своим отдельным кодом, `node:fs` не использует, поэтому импорты внутри пакетов не резолвятся. Этот импортёр обходит проблему: он сам превращает импорт в существующий файл (`canonicalize`) и отдаёт его содержимое через `readFileSync` (`load`).
Работает и в `node-modules`-проектах — там `require` без PnP-хуков, но тот же резолв остаётся корректным.
## Установка
```bash
yarn add -D @advdominion/sass-importer
```
## Использование
```js
import { createImporter } from '@advdominion/sass-importer';
const require = createRequire(import.meta.url);
const importer = createImporter(require);
const { css } = compiler.compileString(source, {
importers: [importer],
loadPaths: ['src/styles'],
});
```
Импортёр принимает `require` из `createRequire(import.meta.url)`: в PnP он несёт PnP-хуки, в `node-modules` — обычный `require`. Это позволяет резолвить и пакеты, и их внутренние относительные импорты одинаково надёжно.
## API
### `createImporter(require): Importer`
- `require` — функция резолва (обычно `createRequire(import.meta.url)`), используется для ветки `pkg:`.
- Возвращает объект из двух колбэков для `sass-embedded`: `canonicalize` и `load`.
`canonicalize(url, ctx)` превращает импорт в канонический `file://` URL или возвращает `null` («я не знаю такой файл» — тогда Sass перебирает следующие импортёры/`loadPaths`).
`load(canonicalUrl)` отдаёт содержимое файла и его синтаксис (`syntax: 'scss'`). Если файла нет — бросает `Error: Stylesheet not found: <путь>`.
## Правила резолва
| Приоритет | Вариант | Пример |
| --------- | ------------------ | --------------------------------------- |
| 1 | Точное совпадение | `@use 'variables'``variables` (файл) |
| 2 | Файл с расширением | `@use 'variables'``variables.scss` |
| 3 | Каталог с `_index` | `@use './lib'``./lib/_index.scss` |
| 4 | Каталог с `index` | `@use './lib'``./lib/index.scss` |
Для каталогов берутся `_index` и `index` как с `.scss`, так и с `.css`. Порядок проверок соответствует порядку перебора Sass, поэтому первая найденная запись — приоритетная.
## Виды импортов
- `pkg:@scope/pkg/path/to/file``require.resolve` + резолв кандидата.
- `file:///abs/path` → кандидат по файловому пути.
- относительные (`../../variables`) → склейка с каталогом содержащего файла (берётся из `ctx.containingUrl`), затем резолв кандидата.
- абсолютные (`/...`) и незнакомые схемы (`http:`, `data:`) → отдаёт `null`, резолв остаётся на Sass.
## Лицензия
MIT