A few notes on coding conventions used in this project.
Run these commands to perform type and style checks and automatically reformat code based on conventions:
make check # Type check + lint frontend and backend
make fmt # Format all code// Use relative imports by default (include .ts extension)
import { Space } from "./space.ts";
// Use `type` for type-only imports (part of the lint rules)
import type { Command } from "./types/command.ts";
// Use package-prefixed absolute when available (defined in package.json)
import { sleep } from "@silverbulletmd/silverbullet/lib/async";- Use
typeoverinterfacefor object shapes - Use
typefor unions and complex types - Always type function parameters explicitly
- Type return values for public APIs
- Let TypeScript infer obvious variable types
- Use
anyfor error objects in catch blocks
- Variables & functions:
camelCase - Classes:
PascalCase - Files:
snake_case.ts(e.g.,http_space_primitives.ts) - Test files:
*.test.tsalongside source files - Types:
PascalCase(e.g.,PageMeta,SpacePrimitives) - Constants:
camelCase
TypeScript tests run with Vitest (Node.js).
Test structure:
import { expect, test } from "vitest";
test("Test description in plain English", async () => {
// Setup
const kv = new MemoryKvPrimitives();
const space = new Space(new DataStoreSpacePrimitives(kv), eventHook);
// Execute
await space.writePage("test", testPage);
// Assert
expect(await space.readPage("test")).toEqual(testPage);
});Assertions:
import { expect } from "vitest";
expect(actual).toEqual(expected);
expect(actual).not.toEqual(notExpected);
expect(value).toBeTruthy();
expect(value).toBeInstanceOf(ClassName);Running tests:
npx vitest run # Run all tests once
npx vitest # Run in watch mode
npx vitest run <file> # Run specific test fileUse JSDoc for public APIs:
/**
* Lists all pages (files ending in .md) in the space.
* @param unfiltered - Whether to include filtered pages
* @returns A list of all pages represented as PageMeta objects
*/
export function listPages(unfiltered?: boolean): Promise<PageMeta[]> {
return syscall("space.listPages", unfiltered);
}Inline comments:
- In case of doubt: add comments around the why of the code (not what)
- Add TODO comments for known issues
// Note: these events are dispatched asynchronously (not waiting for results)
this.eventHook.dispatchEvent(...);
// TODO: Clean this up, this has become a god class
export class Client {Use void to mark intentionally fire-and-forget promises. This satisfies the noFloatingPromises lint rule and signals that the unawaited call is deliberate.
Definite assignment for late initialization:
class Client {
space!: Space; // Initialized in init()
editorView!: EditorView;
}Destructuring:
const { name, def } = command;
const { text, meta } = await this.space.readPage(name);Follow common Go conventions. make fmt also reformats Go code.
See website/Space Lua/Conventions