mf-mono-project-graph
v1.0.4
Published
CLI and graph builder for React/TS monorepos: dependency graph, FSD support, symbol-level analysis, Cursor AI integration.
Maintainers
Readme
mf-mono-project-graph / react-graph
CLI и программная библиотека для построения графа зависимостей React/TypeScript‑монорепо с FSD-архитектурой и tsconfig aliases. Строит единый граф по репозиторию, сохраняет его в .graph/project-graph.json и даёт быстрые структурные ответы через CLI без повторного чтения кода.
Кратко: чем полезен граф
Граф выступает картой фронтенд‑монорепо: помогает сузить десятки файлов «наугад» до нескольких релевантных и экономит токены/время агента.
Оценки пользы из реального опыта работы агента с фронтенд‑монорепо:
| Задача | Оценка | |--------|--------| | Impact-анализ: что затронет изменение X | 9/10 | | Состав страницы/виджета: из каких файлов состоит экран | 9/10 | | Навигация по роутингу: какие страницы есть, какие entry-файлы | 8/10 | | Зависимости фичи от entities/widgets | 8/10 | | Изменение API модуля: кто импортирует и кому обновить контракт | 8/10 | | FSD-рефакторинг: перенос слоя, проверка cross-слайс границ | 8/10 | | Оценка объёма правок перед задачей | 7/10 | | Поиск мёртвого кода и неиспользуемых модулей | 7/10 | | Онбординг: как устроен сервис X | 7/10 | | Связи компонент–хук на symbol-уровне | 6/10 |
Общая полезность для агента в фронтенд-монорепо: 8/10.
Граф сильнее всего помогает в структурных задачах (зависимости, impact, границы приложений, рефакторинг, оценка объёма). Для тонкостей логики и дебага по‑прежнему нужно читать код, но граф существенно сужает область поиска.
Установка
npm install mf-mono-project-graphTypeScript
>=5.0.0— optional peer dependency.
Пакет объявляет"peerDependencies": { "typescript": ">=5.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } }и использует TS Compiler API из зависимостей проекта. npm не тянет свою копию TypeScript и не требует его наличия принудительно, но без TypeScript
>=5.0.0в проекте сборка графа работать не будет.
КонфигурацияpeerDependencies+peerDependenciesMeta.optionalпомогает мягче интегрировать пакет в существующее дерево зависимостей монорепо, но не гарантирует отсутствие предупрежденийnpm warn ERESOLVE overriding peer dependency.
Монорепо (npm workspaces)
В монорепо с несколькими воркспейсами рекомендуется устанавливать пакет в корне:
npm install mf-mono-project-graph --save-devПри установке в реальном монорепо предупреждения ERESOLVE overriding peer dependency могут появляться и зависят от конфигурации всего проекта (версий TypeScript и других пакетов с peerDependencies).
Если при установке ты видишь такие предупреждения, имеет смысл проверить через npm ls typescript и npm ls --all пакеты с конфликтующими peerDependencies по TypeScript и связанным библиотекам — источник конфликта может быть как в mf-mono-project-graph, так и в других зависимостях.
Быстрый старт
1. Инициализировать Cursor-файлы
npx react-graph init-cursor --root .Копирует три шаблона в .cursor/rules/, .cursor/skills/ и .cursor/agents/ — настраивает главного агента Cursor на работу с графом в двух режимах: через субагента graph-agent или напрямую через skill.
2. Собрать граф
npx react-graph build --root . --jsonГраф сохраняется в одном месте — корень/.graph/project-graph.json. Для монорепо всегда указывай --root на корень репозитория (не на каталог приложения/микрофронта). Пересобирай после изменений в services/** или package.json.
3. Задать вопрос к графу
Например, посмотреть, кто зависит от файла/символа:
npx react-graph who-uses "<nodeId>" --root . --jsonCLI
Общие флаги
| Флаг | Алиас | Команды | Описание |
|------|-------|---------|----------|
| --root <path> | -r | все | Корень проекта (по умолчанию: cwd). В монорепо — только корень репо: граф один (.graph/ в корне), без дублей в микрофронтах. |
| --json | — | все | JSON-вывод вместо текстового |
| --resolve-packages | -R | deps, node-info | Развернуть внутренние package:* в конкретные файлы |
| --files-only | -f | deps, node-info | Вернуть только файловые узлы, без package:* |
| --partial | -p | find | Поиск по подстроке вместо точного совпадения имени символа |
| --slice <slice> | — | find | Фильтр по FSD-слайсу (например, entityDrawer, projectData) |
build
npx react-graph build --root <path> [--json]Строит/обновляет граф. JSON-вывод: { rootDir, nodes, edges, durationMs, graphSizeBytes }.
deps
npx react-graph deps "<nodeId>" --root <path> [--json] [-R] [-f]Прямые и транзитивные зависимости узла.
{
"node": "<nodeId>",
"directDependencies": ["..."],
"transitiveDependencies": ["..."]
}who-uses
npx react-graph who-uses "<nodeId>" --root <path> [--json]Обратные зависимости — кто зависит от указанного узла. Используется для impact-анализа.
{ "node": "<nodeId>", "users": ["..."] }node-info
npx react-graph node-info "<nodeId>" --root <path> [--json] [-R] [-f]Всё в одном: тип узла, прямые/транзитивные зависимости, пользователи, пакеты.
find
npx react-graph find "<name>" --root <path> [--json] [--layer <fsd-layer>] [--mf <mf-name>] [--kind <component|hook|function>] [--partial|-p] [--slice <slice-name>]Ищет symbol-узлы в графе по имени символа (компонент/хук/функция) без знания пути к файлу. Возвращает nodeId, который можно передавать в deps, who-uses, node-info.
| Флаг | Описание |
|------|----------|
| --layer <layer> | Фильтр по FSD-слою: pages, widgets, features, entities, shared, app |
| --mf <name> | Фильтр по имени микрофронта (подстрока): gantt-v2-app, host-app |
| --kind <kind> | Фильтр по типу символа: component, hook, function |
| --partial, -p | Поиск по подстроке вместо точного совпадения: find "Gantt" --partial найдёт GanttComponent, GanttHeader и т.п. |
| --slice <slice> | Фильтр по FSD-слайсу: entityDrawer, projectData — для сужения внутри слоя |
JSON-вывод (точный поиск):
{
"query": "ProjectData",
"filters": { "layer": "pages", "mf": "gantt-v2-app" },
"count": 1,
"matches": [
{
"nodeId": "symbol:/abs/.../ProgectData/index.tsx#ProjectData",
"type": "symbol",
"symbolKind": "component",
"layer": "pages",
"slice": "projectData",
"mf": "gantt-v2-app",
"filePath": "/abs/.../ProgectData/index.tsx"
}
]
}При count: 0 в ответе только query, filters, count, matches (пустой массив). Используй --partial для поиска по подстроке или уточни фильтры.
Примеры использования:
# Точный поиск (по умолчанию) — MainLayout есть во всех МФ, уточняем через --mf
npx react-graph find "MainLayout" --root . --json --mf reports-app
# Частичный поиск — найти всё связанное с "Gantt"
npx react-graph find "Gantt" --root . --json --partial --mf gantt-v2-app
# Поиск хуков в конкретном слайсе
npx react-graph find "use" --root . --json --partial --layer entities --slice entityDrawer
# Получаем nodeId и дальше работаем через deps/who-uses
npx react-graph deps "symbol:/abs/.../MainLayout.tsx#MainLayout" --root . --json --files-onlyinit-cursor
npx react-graph init-cursor [--root <path>]Копирует три шаблона в целевой проект (создаёт каталоги при необходимости):
| Файл | Назначение |
|------|------------|
| .cursor/rules/react-graph.mdc | Правило для главного агента: когда и как использовать граф, два режима работы, CLI API |
| .cursor/skills/react-graph.mdc | Skill для прямой работы агента с графом (по явному запросу пользователя) |
| .cursor/agents/graph-agent.md | Суб-агент graph-agent: flow из шагов, правило «только CLI», прозрачный summary ответа |
Модель графа
Простая ASCII-схема для интуиции:
project
|
package:app/*
/ \
file (page) file (widget)
\ /
symbol (component)Типы узлов
| Тип | ID | Ключевые meta-поля |
|-----|----|--------------------|
| project | project:<absPathToRoot> | rootDir |
| file | <absPath/to/file.tsx> | filePath, layer, slice |
| package | package:<name> | — |
| symbol | symbol:<absPath>#<ExportName> | symbolName, symbolKind, exportKind, loc, layer, slice |
FSD-слои (meta.layer): app / pages / widgets / features / entities / shared / unknown.
package:* бывают:
- Внешние npm:
package:react,package:@reduxjs/toolkit - Внутренние alias-пакеты (FSD-слои):
package:widgets/*,package:entities/*,package:features/*,package:pages/*,package:app/*,package:shared/* - Приложения (из router config):
package:app/<appName>сmeta.kind: "app" - Страницы (из router config):
package:pages/<slice>сmeta.kind: "page",routeId,routePath
Типы рёбер
| Тип | Направление | Смысл |
|-----|-------------|-------|
| imports | file → file/package | Файл импортирует другой файл или пакет |
| depends_on | project → package, package:* → file | Зависимость из package.json; резолв alias-пакета в файлы |
| belongs_to_pkg | package:* → file | Файл входит в состав alias-пакета |
| contains | file → symbol | Файл содержит символ |
| exports_symbol | file → symbol | Файл экспортирует символ |
| calls | symbol → symbol | Символ вызывает другой символ |
| uses_jsx | symbol → symbol | Символ использует другой символ как JSX-компонент |
Форматы nodeId
# Файл
/abs/path/src/features/auth/ui/LoginForm.tsx
# npm-пакет
package:react
package:@reduxjs/toolkit
# Внутренний alias-пакет (FSD)
package:widgets/digest
package:entities/user
package:app/host-app
# Символ
symbol:/abs/path/src/widgets/Header/ui/Header.tsx#Header
symbol:/abs/path/src/app/router/AppRouter.tsx#defaultПаттерн работы с агентом
Правило: сначала граф, потом точечное чтение кода.
# 1. Пересобрать граф (если менялись файлы)
npx react-graph build --root . --json
# 2. Получить структуру
npx react-graph node-info "<nodeId>" --root . --json --resolve-packages --files-only
# 3. Прямые зависимости (для понимания скоупа правок)
npx react-graph deps "<nodeId>" --root . --json --resolve-packages --files-only
# 4. Обратные зависимости (impact-анализ)
npx react-graph who-uses "<nodeId>" --root . --jsonПо JSON-ответу агент выбирает минимальный набор файлов и читает только их.
Cursor-интеграция
После npx react-graph init-cursor --root . в проекте появляются три файла:
| Файл | Назначение |
|------|------------|
| .cursor/rules/react-graph.mdc | Правило для главного агента (alwaysApply): когда граф обязателен, два режима работы, CLI API с контрактами |
| .cursor/skills/react-graph.mdc | Skill: прямая работа агента с графом по явному запросу пользователя |
| .cursor/agents/graph-agent.md | Суб-агент graph-agent: flow, правило «только CLI», прозрачный summary CLI-вывода |
Поддерживаются два режима: субагент (главный агент спрашивает разрешение пользователя, делегирует graph-agent) и skill (агент работает с графом напрямую по явной просьбе). Граф пересобирается каждый раз перед началом работы.
Интеграция через npm-скрипты
Добавь в package.json проекта для удобного вызова из корня:
"scripts": {
"graph:build": "react-graph build --root . --json",
"graph:deps": "react-graph deps --root .",
"graph:who-uses": "react-graph who-uses --root .",
"graph:node-info": "react-graph node-info --root .",
"graph:find": "react-graph find --root ."
}Примеры:
npm run graph:node-info -- "/abs/path/DigestPage.tsx" --json --resolve-packages --files-only
npm run graph:find -- "MainLayout" --json --mf reports-appПрограммный API
import { buildGraph, loadGraph, QueryEngine, type ProjectGraph } from "mf-mono-project-graph";
const graph = buildGraph({ rootDir: "/abs/path/to/frontend" });
const engine = new QueryEngine(graph);
engine.deps(nodeId, { transitive: true, resolvePackages: true });
engine.whoUses(nodeId);Ограничения
- Barrel-файлы и entry-точки (
entities/*/index.ts,pages/*/index.tsx) могут иметь мало входящихimports-рёбер, потому что на них ссылаются через alias-пакеты, а не напрямую. Для поиска мёртвого кода дополнительно смотриbelongs_to_pkg. - Symbol-уровень неполный: рёбра
calls/uses_jsxстроятся только для верхнеуровневых функций/хуков/компонентов; namespace-импорты, динамические вызовы и обращения к методам объектов могут не давать рёбер. - Module Federation remotes (
import("digest/DigestApp")) отражаются какpackage:digest/DigestApp— внутренняя структура remote-модулей не сканируется. - Конфиги и декларации (
webpack.config.js,*.d.ts) почти не имеют входящих рёбер: пустойwho-usesне означает мёртвый код. - Router config детектируется только по паттерну
**/app/router/config.*; нестандартные пути не покрываются. - Установка и peer-зависимости TypeScript — пакет использует
peerDependencies+peerDependenciesMeta.optionalдляtypescript >=5.0.0, чтобы не тянуть свою копию TS и лучше вписываться в монорепо. Тем не менее, при установке в проектах с конфликтующимиpeerDependenciesпо TypeScript и другим библиотекам возможны предупрежденияnpm warn ERESOLVE overriding peer dependency; это известное ограничение, а не гарантированно устранённая проблема. - Сам граф весит ~10 MB JSON — целиком в контекст агента не тянется; работа идёт через CLI и уже отфильтрованный вывод.
Troubleshooting
Permission denied при запуске CLI (
npx react-graph)- В версиях
<1.0.1бинарьdist/bin/cli.jsмог публиковаться без права на выполнение, из-за чего запускreact-graphзаканчивался ошибкойPermission denied. - Начиная с
>=1.0.1проблема исправлена: скрипт сборкиscripts/copy-bin.jsвыставляетchmod 755дляdist/bin/cli.js, а в>=1.0.2добавлен хукprepack, который гарантирует запуск сборки перед публикацией. - Если видишь
Permission deniedв реальном проекте, достаточно обновить пакет до актуальной версииmf-mono-project-graph; ручнойchmodвnode_modulesне требуется.
- В версиях
Links
- Repository:
https://github.com/AlekseyGogolev/graph_agent - npm:
https://www.npmjs.com/package/mf-mono-project-graph
