Reference

Dependency rules

Which layer of @skaile/forge-editor may import which, and how the bundle isolation check enforces it.

Imports inside packages/editor/src follow one direction; the entry checks enforce them on the emitted bundles.

LayerMay importMust not import
core, settingseach other, external packages other than vue, @tiptap/vue-3, @nuxt/ui, react, sonnerthose, vue/, react/, any feature, integration, outline, tree, preset, nuxt, server
vue (src/vue: EditorSurface, EditorToolbar, settings components, createEditor, useEditorUi, vueSuggestionRenderer, the queued notifier; SimpleEditor/AdvancedEditor also the preset they mount)core, settings, Vue, @tiptap/vue-3, Nuxt UIreact, nuxt, server
react (src/react: EditorSurface, EditorToolbar, settings components, EditorContextProvider, reactSuggestionRenderer, reactNotifier, vendored shadcn primitives in ui/; SimpleEditor/AdvancedEditor also the preset they mount)core, settings, React, @tiptap/react, Radix, sonner, @iconify/react, framework-free features and presetsvue, @tiptap/vue-3, Nuxt UI, nuxt, server
outline, outline/vue, outline/reactcore (and its layer)tree, composites, any feature
search, search/vue, search/reactcore (and its layer)tree, outline, composites, any feature
change-sets, change-sets/vue, change-sets/reactcore (and its layer)tree, outline, search, composites, any feature
features (media, mentions, transcript, authors, comments, composites, ai, collab, block-ui, themes): features/<x>/{index,feature,model…}.tscore, settings, their own schema/protocol; no UI frameworkanother feature (by implementation), integrations, tree, vue, react, nuxt, server
transfer (features/transfer: the block drop target, its menu, copyTransform, BlockTransform/TransferEnv, the payload plumbing)core and the composites/schema entryany other feature by implementation, integrations, tree, a UI framework in its framework-free files. Its AI transforms (summaryTransform, bulletsTransform and their tasks) live in integrations/composite-ai; composeEditor (a preset) adds them to the transfer row when ai is on
feature bindings (features/<x>/vue.ts + vue/ components, features/<x>/react.ts + react/ components)the feature's framework-free files, core, settings, and that framework's layer (vue or react)the other framework, other features' implementations, nuxt, server
integrationsthe features they connect (framework-free feature entries, or only their schema)other integrations' internals, a UI framework, nuxt, server
tree (index, tree-view-model, store; vue.ts; react.ts)core, outline, composites, media (the bindings also their framework's layer)AI, collab, transcript, mentions, block-ui, integrations; the other framework
presets (presets/compose framework-free; vue/compose.ts, react/compose.ts bind it)everything they composenuxt, server
adapters/direct-ai (./adapters/direct-ai, the browser-direct EditorBackend['ai'])core (BackendError, backend types), settings values, the client-safe protocols features/ai/protocol and integrations/transcript-ai/protocol, the platform fetchserver, the AI SDK (ai, @ai-sdk/*), an HTTP client, vue, react, nuxt, other adapters
nuxt, serverNitro/Nuxt, framework-free schema entries, the client-safe features/ai/protocol; the composite modules of the exception belowbrowser components, any other feature implementation

Exception: server imports framework-free composite modules. The server parses and converts Markdown and projects Blocks without an editor, so it needs more of features/composites than its schema entry. server/document-mutator.ts imports block-types and convert, server/markdown-blocks.ts imports block-types, fences and reference-text, server/block-index.ts imports block-types and fences, server/workspace-tools.ts imports the block-types type, and server/stored-content.ts imports legacy (the older precedent). These modules and the composite modules they pull in (nodes, heading-composites, meta-keys, reference) import only core, settings, i18n, TipTap's framework-free packages (@tiptap/core, @tiptap/pm, @tiptap/suggestion) and each other, never Vue, React, the DOM or another feature. The exception covers exactly them; a server module that needs another composite module or another feature implementation extends it only by an edit here. (server/extract.ts also takes a type from features/media/assets; a type import carries no code into the server bundle.)

Simple and advanced bundles are separate: the simple bundle contains no feature implementation, integration, tree, AI SDK, Yjs, y-tiptap, Hocuspocus, Nuxt or server module; a feature fixture contains core, settings and that feature only. The core-headless fixture builds an editor from /core alone (createEditorContext, coreContentFeature(), mentionsFeature()) and fails on any vue, @vue, @tiptap/vue-3, @nuxt/ui or react module; the headless-<x> fixtures do the same for the framework-free entry of each feature, integration and the tree.

The React fixtures (react-simple, <x>-react, tree-react, outline-react, advanced-react) mount their React binding with its toolbar and fail on any vue, @vue, @tiptap/vue-3, @nuxt/ui, src/vue or .vue module; the Vue fixtures fail on react-dom, sonner and src/react. advanced-react mounts the complete AdvancedEditor (/react/advanced) and requires every feature and integration binding, so no .vue file can slip into the React bundle unnoticed.

Run typecheck:editor, check:editor-entries and check:editor-composition as described in Commands.

check:editor-entries collects the module IDs of every emitted chunk (including dynamic ones) and fails on a forbidden module, on a source module outside the fixture's allow-list, or on a missing required module. To check your own composition, add packages/editor/checks/fixtures/<name>/ (an index.html plus main.ts/main.tsx that mounts the surface with its toolbar, so unused factories cannot be tree-shaken away) and one FIXTURES entry with forbid, require and allowSource patterns (and plugins: 'react' for a React fixture) in packages/editor/checks/entries.ts. Fixtures exist for simple, each feature (Vue, -react and headless- variants), each integration, tree, tree-react, headless-tree, advanced, advanced-react, the headless core (core-headless), the React simple editor (react-simple) and the Node schema.

Bundle isolation proof

bun run check:editor-entries builds each fixture in packages/editor/checks/fixtures/<name>/ as its own production Vite app, collects the module IDs of every emitted chunk and fails on forbidden modules (or on a missing required one, so exported factories cannot be tree-shaken away unnoticed). Last result for simple: 325 modules, JS 1082.5 kB (gzip 331.1 kB), CSS 194.5 kB (gzip 25.2 kB); for tree: 433 modules, JS 1314.5 kB (gzip 389.2 kB), CSS 197.6 kB (gzip 25.7 kB). The code-only and code-only-react fixtures prove the code surface carries CodeMirror and no ProseMirror or TipTap (and nothing of this package outside src/code), and the markdown-vue and tables fixtures prove the Markdown entries contain no CodeMirror. The check also bundles the advanced schema for Node and compares it with a snapshot (checks/schema.ts).

Copyright © 2026