A high-level overview of how the packages in this monorepo relate to each other and how the Language Server Protocol (LSP) server is composed.
- Package Dependency Graph
- LSP Server Composition
- Data Flow
- How-To: Adding a New Pack Type
- How-To: Adding a New Diagnostic Rule
Arrows represent "depends on" relationships (e.g., bedrock-types --> shared means bedrock-types depends on shared).
graph TD
shared["shared<br/><code>bc-minecraft-bedrock-shared</code>"]
project["project<br/><code>bc-minecraft-project</code>"]
types["bedrock-types<br/><code>bc-minecraft-bedrock-types</code>"]
commands["bedrock-commands<br/><code>bc-minecraft-bedrock-command</code>"]
molang["molang<br/><code>bc-minecraft-molang</code>"]
vanilla["bedrock-vanilla-data<br/><code>bc-minecraft-bedrock-vanilla-data</code>"]
bp["bedrock-project<br/><code>bc-minecraft-bedrock-project</code>"]
diagnoser["bedrock-diagnoser<br/><code>bc-minecraft-bedrock-diagnoser</code>"]
ide_shared["ide/shared<br/><code>@blockception/ide-shared</code>"]
server["ide/base/server<br/><code>bc-minecraft-lsp</code>"]
client["ide/base/client<br/><code>bc-minecraft-lsp-client</code>"]
vscode["ide/vscode<br/>VSCode Extension"]
types --> shared
commands --> types
molang --> shared
molang --> types
bp --> shared
bp --> project
bp --> types
bp --> commands
bp --> molang
bp --> vanilla
diagnoser --> shared
diagnoser --> types
diagnoser --> commands
diagnoser --> molang
diagnoser --> vanilla
diagnoser --> bp
server --> diagnoser
server --> bp
client --> ide_shared
vscode --> server
vscode --> client
| Package | npm name | Purpose |
|---|---|---|
packages/shared |
bc-minecraft-bedrock-shared |
Shared utilities: glob matching, JSON parsing helpers, common types |
packages/project |
bc-minecraft-project |
Generic project framework: .mcignore, .mcattributes, project discovery |
packages/bedrock-types |
bc-minecraft-bedrock-types |
TypeScript interfaces and type definitions for all Bedrock concepts |
packages/bedrock-commands |
bc-minecraft-bedrock-command |
Command parsing and validation for .mcfunction files |
packages/molang |
bc-minecraft-molang |
Molang expression parsing, validation, and completion |
packages/bedrock-vanilla-data |
bc-minecraft-bedrock-vanilla-data |
Static vanilla Minecraft data (blocks, items, entities, effects, …) |
packages/bedrock-project |
bc-minecraft-bedrock-project |
Reads and parses Bedrock project files into an in-memory data model |
packages/bedrock-diagnoser |
bc-minecraft-bedrock-diagnoser |
Diagnostic engine: 70+ rule categories across behavior packs, resource packs, and general validation |
ide/shared |
@blockception/ide-shared |
Shared interfaces for IDE integrations (client ↔ server protocol extensions) |
ide/base/server |
bc-minecraft-lsp |
Full LSP server implementation — all language features, processors, and services |
ide/base/client |
bc-minecraft-lsp-client |
LSP client wrapper, handles server lifecycle inside an IDE |
ide/vscode |
(VSCode extension) | Bundles the client and server into the VSCode extension entry point |
setupServer in ide/base/server/src/lsp/server/setup.ts wires together every component:
┌─────────────────────────────────────────────────────────────────────┐
│ setupServer(config) │
│ │
│ connection ──► ExtensionContext ──► ServiceManager │
│ │ │
│ ├── DocumentManager (tracks open documents) │
│ └── Database (ProjectData cache) │
│ │
│ Processor chain (each depends on the next): │
│ WorkspaceProcessor │
│ └── PackProcessor │
│ └── DocumentProcessor │
│ └── DiagnoserService ──► Diagnoser (from │
│ bedrock-diagnoser) │
│ │
│ Feature services (all registered with ServiceManager): │
│ ConfigurationService CodeActionService CodeLensService │
│ CommandService CompletionService DefinitionService │
│ DocumentSymbolService FormatService ImplementationService │
│ ReferenceService SemanticsServer SignatureService │
│ TypeDefinitionService WorkspaceSymbolService DataSetService │
└─────────────────────────────────────────────────────────────────────┘
Processor responsibilities:
| Component | Responsibility |
|---|---|
WorkspaceProcessor |
Listens for workspace-level events; iterates workspace folders and delegates to PackProcessor |
PackProcessor |
Discovers packs via manifests; iterates all files in a pack and delegates to DocumentProcessor |
DocumentProcessor |
Hooks onDidOpen / onDidSave; calls ProjectData.process() then DiagnoserService.diagnose() |
DiagnoserService |
Bridges the LSP layer to the Diagnoser from bedrock-diagnoser; calls connection.sendDiagnostics() |
The full path from a file save to diagnostics appearing in the editor:
User saves file
│
▼
documents.onDidSave (DocumentManager)
│
▼
DocumentProcessor.onDocumentChanged(e)
│
├──► DocumentProcessor.process(doc)
│ │
│ ▼
│ Glob.isMatch() check against .mcignore patterns
│ │
│ ▼ (file is not ignored)
│ database.ProjectData.process(doc)
│ │
│ ▼
│ PackType.detect(doc.uri) ──────────────┐
│ │ │
│ ▼ (e.g. behavior_pack) │ (resource_pack / world / …)
│ behaviorPacks.process(doc) └─► equivalent collection
│ + extra preprocessing for commands
│
└──► DocumentProcessor.diagnose(doc)
│
▼
DiagnoserService.diagnose(doc)
│
│ (skipped if workspace not yet fully traversed)
▼
Diagnoser.process(doc) [bedrock-diagnoser]
│
▼
PackType.detect(doc.uri)
│
├── behavior_pack ──► BehaviorPack.diagnose_document(diagnoser)
├── resource_pack ──► ResourcePack.diagnose_document(diagnoser)
├── skin_pack ──► SkinPack.diagnose_document(diagnoser)
└── world ──► WorldPack.diagnose_document(diagnoser)
│
▼
diagnoser.done()
│
▼
InternalContext fires onDiagnosingFinished
│
▼
DiagnoserService.set(doc, diagnostics)
│
▼
connection.sendDiagnostics({ uri, diagnostics })
│
▼
Diagnostics appear in the editor ✓
Follow these steps to introduce a new first-class pack type (e.g., addon_pack).
packages/bedrock-project/src/project/pack-type.ts
export enum PackType {
resource_pack,
behavior_pack,
skin_pack,
world,
addon_pack, // ← add new value
unknown,
}
export namespace PackType {
// Add a detection regex for the new pack's folder-name convention
export const AddonPackMatch: RegExp = /[/\\].*(addon([ _-]|)pack|ap).*[/\\]/i;
export function detect(uri: string): PackType {
if (BehaviorPackMatch.test(uri)) return PackType.behavior_pack;
if (ResourcePackMatch.test(uri)) return PackType.resource_pack;
if (WorldMatch.test(uri)) return PackType.world;
if (SkinPack.test(uri)) return PackType.skin_pack;
if (AddonPackMatch.test(uri)) return PackType.addon_pack; // ← add here
return PackType.unknown;
}
}Mirror an existing collection (e.g., packages/bedrock-project/src/project/resource-pack/) to create:
packages/bedrock-project/src/project/addon-pack/
addon-pack.ts # Pack class implementing Pack interface
addon-pack-collection.ts # Collection class with add() / process() / delete()
index.ts # Re-exports
packages/bedrock-project/src/project/project-data.ts
export class ProjectData {
// Add the new collection
addonPacks: AddonPackCollection = new AddonPackCollection();
process(doc: TextDocument): DataSetBase | undefined {
switch (PackType.detect(doc.uri)) {
// ...existing cases...
case PackType.addon_pack:
return this.addonPacks.process(doc); // ← add case
}
}
addPack(manifest: string, project: MCProject): Pack | undefined {
switch (types) {
// ...existing cases...
case PackType.addon_pack:
return this.addonPacks.add(parent, context, effectiveManifest); // ← add case
}
}
}Create a diagnostic entry-point:
packages/bedrock-diagnoser/src/diagnostics/addon-pack/
addon-pack.ts # Export diagnose_document(diagnoser) function
index.ts
Then register the dispatch in packages/bedrock-diagnoser/src/types/diagnoser.ts:
switch (pack.type) {
// ...existing cases...
case PackType.addon_pack:
out = AddonPack.diagnose_document(diagnoser); // ← add case
break;
}ide/base/server/src/lsp/process/pack-processor.ts
remove(pack: Pack) {
const pd = this.extension.database.ProjectData;
pd.deleteFolder(pack.folder);
this.removePack(pd.behaviorPacks.packs, pack.folder, pd.behaviorPacks.delete);
this.removePack(pd.resourcePacks.packs, pack.folder, pd.resourcePacks.delete);
this.removePack(pd.worlds.packs, pack.folder, pd.worlds.delete);
this.removePack(pd.addonPacks.packs, pack.folder, pd.addonPacks.delete); // ← add
}Completion providers that switch on PackType (e.g., in ide/base/server/src/lsp/completion/) may need new cases for the new pack type.
For a detailed reference with examples see Creating Diagnostics.
| Rule type | Directory |
|---|---|
| Behavior pack | packages/bedrock-diagnoser/src/diagnostics/behavior-pack/ |
| Resource pack | packages/bedrock-diagnoser/src/diagnostics/resource-pack/ |
| Generic validation | packages/bedrock-diagnoser/src/diagnostics/general/ |
| Minecraft concepts | packages/bedrock-diagnoser/src/diagnostics/minecraft/ |
| Molang | packages/bedrock-diagnoser/src/diagnostics/molang/ |
// packages/bedrock-diagnoser/src/diagnostics/behavior-pack/entity/my-check.ts
import { DiagnosticsBuilder, DiagnosticSeverity } from '../../../types';
export function diagnose_my_check(value: string, diagnoser: DiagnosticsBuilder): void {
if (isValid(value)) return;
diagnoser.add(
value,
`Cannot find my-resource: ${value}`,
DiagnosticSeverity.error,
'behaviorpack.entity.my_resource.missing', // follows category.subcategory.type.detail
);
}Find the diagnose_document function for the relevant file type (e.g., entity.ts) and add a call to your new function.
<category>.<subcategory>.<type>.<detail>
Common suffixes: .missing, .invalid, .deprecated, .minimum, .maximum, .duplicate
// packages/bedrock-diagnoser/test/diagnostics/behavior-pack/entity/my-check.test.ts
import { TestDiagnoser } from '../../../../diagnoser';
describe('my-check', () => {
it('reports an error for a missing resource', () => {
const diagnoser = TestDiagnoser.createDocument(/* ... */);
diagnose_my_check('nonexistent_id', diagnoser);
expect(diagnoser.count('behaviorpack.entity.my_resource.missing')).toBe(1);
});
});Run with:
npm test --workspace=packages/bedrock-diagnoser