Lab
Registry
type Shortcode = (text: string) => string;
// The registry: shortcodes by name, filled at start-up, then sealed.
class ShortcodeRegistry {
private entries = new Map<string, Shortcode>();
private sealed = false;
register(name: string, shortcode: Shortcode): void {
if (this.sealed) throw new Error(`registration is closed: ${name}`);
if (this.entries.has(name)) throw new Error(`already registered: ${name}`);
this.entries.set(name, shortcode);
}
resolve(name: string): Shortcode {
const shortcode = this.entries.get(name);
if (!shortcode) throw new Error(`unknown shortcode: ${name}`);
return shortcode;
}
names(): string[] {
return [...this.entries.keys()].sort();
}
seal(): void {
this.sealed = true;
}
}
// What the theme and a plugin contribute. The renderer knows none of them.
function installTheme(registry: ShortcodeRegistry) {
registry.register('note', (text) => `<aside>${text}</aside>`);
registry.register('code', (text) => `<pre>${text}</pre>`);
}
function installKeyboardPlugin(registry: ShortcodeRegistry) {
registry.register('kbd', (text) => `<kbd>${text}</kbd>`);
}
// The page renderer: one tag's name and inner text in, a fragment out.
function render(registry: ShortcodeRegistry, tag: string, text: string) {
try {
return registry.resolve(tag)(text);
} catch (error) {
return `[${(error as Error).message}]`;
}
}
// Bootstrap: every contributor registers, then setup closes.
const registry = new ShortcodeRegistry();
installTheme(registry);
// A second theme, also enabled, ships its own note.
let clash = 'none';
try {
registry.register('note', (text) => `<div class="callout">${text}</div>`);
} catch (error) {
clash = (error as Error).message;
}
installKeyboardPlugin(registry);
registry.seal();
const picker = registry.names();
const noteHtml = render(registry, 'note', 'Back up first');
const kbdHtml = render(registry, 'kbd', 'Ctrl');
const missing = render(registry, 'quote', 'Hi');
console.log(`picker: ${picker.join(', ')}`);
console.log(`note: ${noteHtml}`);
console.log(`kbd: ${kbdHtml}`);
console.log(`quote: ${missing}`);
console.log(`second note: ${clash}`);
TODO
Visual to come