72 lines
4.2 KiB
Markdown
72 lines
4.2 KiB
Markdown
# @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 { createRequire } from 'node:module';
|
||
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
|