npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-tiers

v0.1.2

Published

Modular routing system for SvelteKit. Builds the routes directory from multiple sources (tiers) with priorities.

Readme

svelte-tiers

En | Ru

Модульная маршрутизация для SvelteKit. Разделяйте маршруты по нескольким исходным папкам («слоям», tiers) и объединяйте их в единую папку маршрутов на этапе сборки — с точным контролем над тем, какой слой чем «владеет».


Оглавление


Что это делает

Обычный SvelteKit ожидает все маршруты в одной папке (src/routes). Это становится неудобным, когда большое приложение хочется разбить на самодостаточные модули (например, blog, shop, admin), каждый из которых несёт свои маршруты.

svelte-tiers позволяет объявить несколько исходных папок маршрутов и объединяет их в одну папку, которую затем использует SvelteKit. Вы сохраняете модульную структуру файлов; SvelteKit видит единое плоское дерево маршрутов.

Основные возможности:

  • Несколько источников маршрутов («слоёв») с упорядочиванием по приоритету.
  • Маска {module}, которая автоматически разворачивается на каждую папку модуля.
  • Параметр level, управляющий тем, какой частью своего дерева владеет слой и что могут дополнять слои с более низким приоритетом.
  • Опциональная фильтрация по имени файла через match.
  • Прозрачный passthrough: без настроенных слоёв работает ровно как обычный sveltekit().
  • Hot reload в дев-режиме: изменения в любом слое пересобирают результат и перезагружают страницу.

Установка

npm install -D svelte-tiers
# или
pnpm add -D svelte-tiers
# или
bun add -d svelte-tiers

@sveltejs/kit и vite — это peer-зависимости (они уже есть в проекте SvelteKit).


Быстрый старт

Замените плагин sveltekit() в vite.config.js на svelteTiers():

// vite.config.js
import { defineConfig } from 'vite';
import { svelteTiers } from 'svelte-tiers';

export default defineConfig({
  plugins: [await svelteTiers()],
});

Затем объявите слои в svelte.config.js:

// svelte.config.js
import adapter from '@sveltejs/adapter-auto';

export default {
  kit: {
    adapter: adapter(),
    tiers: [
      'src/routes',
      'src/modules/{module}/routes',
    ],
  },
};

Теперь такая структура:

src/
  routes/
    +layout.svelte
    +page.svelte
  modules/
    blog/
      routes/
        blog/+page.svelte
    shop/
      routes/
        shop/+page.svelte

…объединяется в единое дерево маршрутов, которое обслуживает SvelteKit.

svelteTiers()асинхронная функция, всегда используйте await (или вызывайте её внутри асинхронной функции конфигурации).


Как это работает

  1. При старте svelteTiers() читает конфигурацию kit (либо переданный вами объект, либо из svelte.config.js).
  2. Если tiers задан (непустой), он:
    • разрешает каждый слой, разворачивая маски {module};
    • проходит по слоям по порядку и вычисляет объединённую карту файлов согласно правилам приоритета и level;
    • записывает результат в папку слияния (по умолчанию .tiers/routes);
    • указывает files.routes SvelteKit на эту папку;
    • устанавливает dev-watcher Vite, пересобирающий результат при изменениях.
  3. Затем оборачивает и возвращает настоящий плагин (или плагины) sveltekit().

Папка слияния специально находится вне .svelte-kit/ — Vite игнорирует .svelte-kit/ в своём файловом наблюдателе, что сломало бы hot reload объединённых маршрутов.


Конфигурация

Где хранится конфигурация

Есть два варианта:

A. Конфигурация в svelte.config.js (рекомендуется) — вызывайте svelteTiers() без аргументов; он загрузит поле kit из svelte.config.js:

// vite.config.js
plugins: [await svelteTiers()]
// svelte.config.js
export default {
  kit: {
    /* обычные опции kit */
    tiers: [ /* ... */ ],
  },
};

B. Конфигурация прямо в вызове — передайте объект в форме kit:

// vite.config.js
plugins: [
  await svelteTiers({
    adapter: adapter(),
    tiers: ['src/routes', 'src/modules/{module}/routes'],
  }),
]

Объект принимает те же поля, что и kit в svelte.config.js, плюс tiers.

Режим passthrough (прозрачная обёртка)

Важно. Если tiers не задан или задан как пустой массив (tiers: []), плагин не делает ничего особенного — он превращается в прозрачную обёртку над sveltekit().

В режиме passthrough он:

  • не переопределяет files.routes;
  • не создаёт папку .tiers/routes;
  • не запускает watcher слияния;
  • просто передаёт все опции kit в sveltekit().

Поэтому оба варианта ниже ведут себя ровно как прямой вызов sveltekit():

// вообще без ключа tiers
export default { kit: { /* ... */ } };

// либо явный пустой массив
export default { kit: { tiers: [], /* ... */ } };

Режим слияния активируется только тогда, когда tiers содержит хотя бы один элемент. Это позволяет безопасно поставить svelteTiers() как обёртку по умолчанию и включить модульную маршрутизацию позже.

Опция tiers

tiers — это упорядоченный массив. Каждый элемент — это либо строка (сокращённая запись), либо объект:

tiers: [
  // сокращённая запись → { src, level: Infinity, match: null }
  'src/routes',

  // полная форма (объект)
  {
    src: 'src/modules/{module}/routes',
    level: Infinity,
    match: null,
  },
]

| Поле | Тип | По умолчанию | Значение | | ------- | -------------------------- | ------------ | ------------------------------------------------------------------- | | src | string | — | Исходная папка. Может содержать маску {module} (разворачивается по папкам). | | level | number | Infinity | Насколько глубоко слой «захватывает» дерево (см. level). | | match | string \| RegExp \| null | null | Опциональный фильтр по имени файла. null = все файлы. |

Порядок = приоритет. Более ранние слои выигрывают конфликты (см. ниже).

Маска {module}

Литеральный токен {module} внутри src разворачивается на каждую непосредственную вложенную папку, найденную в этой позиции. Например:

src: 'src/modules/{module}/routes'

с папками:

src/modules/blog/routes
src/modules/shop/routes
src/modules/admin/routes

разворачивается в три эффективных слоя — по одному на модуль — все они наследуют одинаковые level и match. Модули обрабатываются вместе как часть этого единого элемента слоя, после всех слоёв, объявленных до него.

Слой без {module} указывает ровно на одну папку.


Приоритет и конфликты

Слои обрабатываются в порядке объявления. Когда два слоя дают один и тот же целевой путь, более ранний слой выигрывает, а более поздний пропускается.

Есть два вида «выигрыша»:

  • file-claim (захват файла) — точный путь файла уже занят слоем с более высоким приоритетом; дубликат из нижнего слоя отбрасывается.
  • dir-claim (захват папки) — целое поддерево папки зарезервировано слоем с более высоким приоритетом; нижние слои не могут добавить туда ничего. Dir-claim возникает из-за level (см. следующий раздел).

Как работает level

level — сердце плагина. Он управляет тем, какая часть дерева слоя считается единым самодостаточным блоком, а какую могут расширять слои с более низким приоритетом.

Глубина отсчитывается с нуля, относительно корня src слоя:

  • файлы прямо в корне имеют глубину 0;
  • файлы/папки на уровень ниже — глубину 1;
  • и так далее.

Правило

  • Файлы выше границы уровня объединяются по отдельности — слой с более низким приоритетом всё ещё может добавить соседние файлы по этим путям, которых пока нет.
  • Каждая папка на границе уровня копируется целиком (со всем поддеревом) и захватывается — ни один слой с более низким приоритетом не может добавить внутрь неё что-либо.

«Берётся целиком и захватывается» означает: всё содержимое папки берётся из этого слоя, и она становится dir-claim, блокирующим нижние слои.

Шпаргалка по level

| level | Поведение | | ---------- | ----------------------------------------------------------------------------- | | 0 | Только файлы прямо в корне. В папки вообще не заходим. | | 1 | Файлы корня + каждая папка верхнего уровня берётся целиком и захватывается. | | 2 | Файлы на глубине 0–1 + каждая папка на глубине 2 берётся целиком/захватывается. | | n | Файлы на глубине 0 … n-1 + каждая папка на глубине n берётся целиком/захватывается. | | Infinity | Всё, рекурсивно. Ни одна папка не захватывается — чистое пофайловое слияние. |

Примеры

Во всех примерах ниже используются два слоя:

tiers: [
  /* слой 1, меняется в каждом примере */,
  'src/modules/{module}/routes',   // слой 2 (более низкий приоритет, level: Infinity)
]

и такие исходники:

src/routes/
  +layout.svelte
  +page.svelte
  blog/
    +page.svelte

src/modules/blog/routes/
  blog/
    +page.svelte        ← конфликтует с blog/+page.svelte слоя 1
    [id]/
      +page.svelte      ← новый
  about/
    +page.svelte        ← новый

Пример A — level: Infinity (чистое слияние)

{ src: 'src/routes', level: Infinity }   // или просто 'src/routes'

Ничего не захватывается как блок. Слой 1 выигрывает точные конфликты; слой 2 заполняет все остальные пути — даже внутри blog/.

Результат в .tiers/routes:

+layout.svelte              (слой 1)
+page.svelte                (слой 1)
blog/+page.svelte           (слой 1 — выигрывает конфликт)
blog/[id]/+page.svelte      (слой 2 — добавлен, конфликта нет)
about/+page.svelte          (слой 2 — добавлен, конфликта нет)

Пример B — level: 1 (захват папок верхнего уровня)

{ src: 'src/routes', level: 1 }
  • +layout.svelte, +page.svelte (глубина 0) — объединяются обычным образом.
  • blog/ (папка на глубине 1) — берётся целиком и захватывается. Слой 2 не может добавить внутрь blog/ ничего.

Результат в .tiers/routes:

+layout.svelte              (слой 1)
+page.svelte                (слой 1)
blog/+page.svelte           (слой 1 — весь blog/ захвачен)
about/+page.svelte          (слой 2 — about/ НЕ захвачен слоем 1)

Отличия от примера A:

  • blog/[id]/+page.svelte из слоя 2 отброшенblog/ захвачен как папка.
  • about/+page.svelte всё же добавлен — у слоя 1 нет папки about/.

Смысл level: 1: «какой раздел (папку верхнего уровня) я определяю — тем владею целиком; но разделы, которые я не определяю, могут добавлять модули».


Пример C — level: 0 (только файлы корня)

{ src: 'src/routes', level: 0 }

Слой 1 добавляет только файлы прямо в корне и никогда не заходит в папки:

+layout.svelte              (слой 1 — файл корня)
+page.svelte                (слой 1 — файл корня)
blog/+page.svelte           (слой 2 — слой 1 не заходил в blog/)
blog/[id]/+page.svelte      (слой 2)
about/+page.svelte          (слой 2)

Здесь слой 1 предоставляет лишь «оболочку» приложения (+layout, корневой +page), а всеми разделами владеют модули.


Пример D — level: 2 (захват глубже)

{ src: 'src/routes', level: 2 }

С исходником:

src/routes/
  +layout.svelte              глубина 0
  blog/
    +page.svelte              глубина 1
    [id]/                     глубина 2  ← папка НА границе
      +page.svelte

Поведение:

  • файлы на глубине 0–1 (+layout.svelte, blog/+page.svelte) — объединяются по отдельности; нижние слои могут добавлять отсутствующие соседние файлы.
  • blog/[id]/ (папка на глубине 2) — берётся целиком и захватывается. Слой 2 не может добавить ничего под blog/[id]/, но может добавить, например, blog/tags/+page.svelte (эта папка не захвачена).

Как выбрать level

| Цель | Значение | | -------------------------------------------------------------------------- | ------------ | | Базовое приложение владеет всем; модули лишь заполняют недостающие файлы | Infinity | | Базовое приложение владеет целыми разделами, которые определяет; модули добавляют новые разделы | 1 | | Базовое приложение владеет только оболочкой; модули владеют всеми разделами | 0 | | Точечный захват на конкретной глубине | 2, 3, … |


Фильтр match

match ограничивает, какие файлы добавляет слой. Папки никогда не фильтруются по match (фильтруются только файлы). Принимает:

  • точное имя файла — строка ('+page.svelte');
  • RegExp (/\.svelte$/);
  • строку в форме /pattern/flags ('/\\+(page|layout)\\.svelte/').
tiers: [
  // добавлять только файлы +page.svelte
  { src: 'src/routes', match: '+page.svelte' },

  // регулярка: только *.svelte
  { src: 'src/modules/{module}/routes', match: /\.svelte$/ },

  // строковая форма регулярки (обратите внимание на экранирование)
  { src: 'src/extra', match: '/\\+(page|layout)\\.svelte/' },
]

match: null (по умолчанию) означает, что проходят все файлы.


Разработка и hot reload

В дев-режиме плагин следит за всеми исходными папками слоёв. Когда вы:

  • меняете файл → результат пересобирается, а затронутые модули инвалидируются в графе модулей Vite;
  • добавляете файл или папку → они попадают в слияние, и страница перезагружается;
  • удаляете файл или папку → они удаляются из слияния, и страница перезагружается.

Небольшой debounce объединяет серии изменений; полная перезагрузка страницы отправляется после того, как манифест маршрутов SvelteKit успел пересобраться — чтобы новые маршруты не отдавали 404 без перезапуска dev-сервера.


Сгенерированная папка

По умолчанию результат слияния пишется в:

.tiers/routes
  • Она генерируется — не редактируйте вручную; ваши правки будут перезаписаны.

  • Добавьте её в .gitignore:

    .svelte-kit/
    .tiers/
  • Она специально находится вне .svelte-kit/ (Vite игнорирует .svelte-kit/ в своём наблюдателе, что сломало бы hot reload объединённых маршрутов).

Местоположение можно переопределить через files.routes (см. ниже).


Полный справочник конфигурации

Все опции передаются внутри объекта kitsvelte.config.js или прямо в svelteTiers()).

{
  kit: {
    // ── все стандартные опции `kit` SvelteKit поддерживаются и пробрасываются ──
    adapter: adapter(),
    alias: { /* ... */ },
    // ...

    // ── специфичные для svelte-tiers ──
    tiers: [
      // сокращённая запись (строка)
      'src/routes',

      // полная форма (объект)
      {
        src: 'src/modules/{module}/routes', // обязательно
        level: Infinity,                     // по умолчанию: Infinity
        match: null,                         // по умолчанию: null
      },
    ],

    // Опционально: переопределить местоположение папки слияния.
    // По умолчанию: '.tiers/routes'. Используется только когда `tiers` непустой.
    files: {
      routes: '.tiers/routes',
    },
  },
}

Поля слоя

| Поле | Тип | Обязательно | По умолчанию | Описание | | ------- | -------------------------- | ----------- | ------------ | -------------------------------------------------------------- | | src | string | да | — | Исходная папка; может включать маску {module}. | | level | number | нет | Infinity | Глубина захвата (с нуля). См. level. | | match | string \| RegExp \| null | нет | null | Фильтр по имени файла. null = все файлы. |

Флаги поведения плагина (вычисляемые, не задаются вами)

| Условие | Результат | | -------------------------------- | ------------------------------------------------------------------- | | tiers отсутствует или [] | Режим passthrough: обычный sveltekit(), files.routes не трогается. | | tiers непустой | Режим слияния: files.routes указывает на папку слияния, watcher включён. | | files.routes задан + слияние | Местоположение папки слияния переопределяется на этот путь. |


Рецепты

Модульное приложение с базовой оболочкой

Базовое приложение владеет layout и разделами верхнего уровня, которые определяет; модули добавляют собственные разделы:

tiers: [
  { src: 'src/routes', level: 1 },
  'src/modules/{module}/routes',
]

Тонкое ядро, маршрутизация от модулей

Ядро предоставляет только +layout и главную страницу; всё остальное — из модулей:

tiers: [
  { src: 'src/routes', level: 0 },
  'src/modules/{module}/routes',
]

Переопределения, которые могут заполнять пробелы

Слой overrides, выигрывающий точные конфликты, но пропускающий всё остальное:

tiers: [
  'src/overrides',                   // высший приоритет, Infinity
  'src/routes',
  'src/modules/{module}/routes',
]

Инъекция только layout-файлов

Слой, добавляющий лишь layout-файлы, оставляя страницы из других источников:

tiers: [
  { src: 'src/layouts', match: /\+layout(\.\w+)?\.(svelte|js|ts)$/ },
  'src/routes',
  'src/modules/{module}/routes',
]

FAQ

Нужно ли создавать файлы в .tiers/routes? Нет. Она генерируется. Редактируйте исходники слоёв (src/routes, src/modules/...).

Что если два модуля определяют один и тот же маршрут? Модули внутри одного слоя {module} имеют одинаковый приоритет. Первый разрешённый выигрывает конфликт точного пути; возможно, будет выведено предупреждение. Реструктурируйте или используйте слой с более высоким приоритетом для устранения неоднозначности.

Можно ли использовать без модульной маршрутизации? Да — это режим passthrough. Не задавайте tiers, и это будет просто sveltekit().

Работает ли с +layout, +page, +server, ...+error, .js/.ts файлами? Да — он объединяет файлы по пути независимо от типа. level/match применяются единообразно.

Влияет ли это на продакшн-сборки? Слияние формируется на этапе конфигурации/старта и при изменениях файлов, поэтому и dev, и build используют одну и ту же сгенерированную папку.

Можно ли смешивать слои с разными level в одной конфигурации? Да. Каждый слой обрабатывается со своим собственным level. Типичный паттерн — базовый слой с level: 0 или 1 и слой модулей с Infinity.

Что произойдёт, если папка модуля пустая? Пустой модуль (или папка, не содержащая подходящих под match файлов) просто ничего не добавляет в результат слияния. Ошибки не возникает.

Можно ли иметь несколько масок {module} в разных слоях? Да. Каждый слой с {module} разворачивается независимо. Например, можно иметь src/modules/{module}/routes и src/plugins/{module}/routes как отдельные слои.


Устранение неполадок

Новые маршруты отдают 404 в dev. Слияние пересобирается и запускает перезагрузку, но манифесту SvelteKit нужен момент. Если совершенно новый маршрут не появился — сохраните ещё раз или перезапустите dev. Убедитесь, что папка слияния — это .tiers/routes (вне .svelte-kit/) — маршруты внутри .svelte-kit/ не отслеживаются Vite и не будут надёжно перезагружаться на лету.

Изменения не подхватываются. Проверьте, что изменённый файл лежит внутри одного из корней tiers[].src и не исключён фильтром match этого слоя.

Маршрут модуля молча отсутствует. Скорее всего, он захвачен как папка (dir-claim) слоем с более высоким приоритетом и низким level. Повысьте level этого слоя или переместите конфликтующую папку. См. Как работает level.

Всё маршрутизируется так, будто слоёв нет. Вероятно, вы в режиме passthrough — проверьте, что tiers присутствует и непустой в той конфигурации, которую фактически загружает плагин.

svelteTiers is not a function / неожиданный промис. svelteTiers() асинхронна — её нужно await-ить в vite.config.js.

Ошибка invalid tier при старте. Элемент слоя должен быть либо строкой, либо объектом с полем src. Проверьте, что все элементы tiers корректны (в частности, у объектной формы задан src).

Ошибка invalid match. Значение match должно быть null, строкой (точное имя или форма /pattern/flags) или RegExp. Другие типы вызывают ошибку.

Конфликт двух модулей — какой выиграл, непредсказуемо. Порядок разворачивания {module} зависит от порядка чтения подпапок. Если вам нужен детерминированный приоритет между конкретными модулями — вынесите приоритетный модуль в отдельный слой выше по списку tiers.


Краткая шпаргалка

// svelte.config.js
export default {
  kit: {
    adapter: adapter(),

    // Нет tiers / tiers: []  → passthrough (как обычный sveltekit())
    // Непустой tiers         → режим слияния в .tiers/routes

    tiers: [
      // 1) строка = { src, level: Infinity, match: null }
      'src/routes',

      // 2) объект = точный контроль
      {
        src: 'src/modules/{module}/routes',
        level: Infinity,   // Infinity | 0 | 1 | 2 | n
        match: null,       // null | '+page.svelte' | /\.svelte$/ | '/.../flags'
      },
    ],
  },
};

| Хочу… | level слоя 1 | | ---------------------------------------------- | ------------ | | Базовое приложение владеет всем, модули заполняют пробелы | Infinity | | Базовое владеет своими разделами, модули добавляют новые | 1 | | Базовое даёт только оболочку, модули владеют разделами | 0 | | Точечный захват на глубине N | N |

Помните:

  • Более ранний слой = более высокий приоритет.
  • level определяет, какие папки слой «захватывает» целиком.
  • Папку .tiers/routes не редактируют — она генерируется.
  • Без tiers плагин = прозрачная обёртка над sveltekit().