This commit is contained in:
Morten Olsen
2024-12-10 20:59:29 +01:00
commit ede2d56b7c
54 changed files with 6955 additions and 0 deletions

2
packages/plugin-markdown/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/node_modules/
/dist/

View File

@@ -0,0 +1,34 @@
{
"name": "@plaindb/plugin-markdown",
"version": "1.0.0",
"type": "module",
"main": "dist/exports.js",
"files": [
"dist"
],
"scripts": {
"build": "tsc --build"
},
"dependencies": {
"@plaindb/plaindb": "workspace:*",
"@plaindb/plugin-core": "workspace:*",
"lodash": "^4.17.21",
"mdast-util-to-string": "^4.0.0",
"remark-behead": "^3.1.0",
"remark-directive": "^3.0.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"unified": "^11.0.5",
"unist-util-remove-position": "^5.0.0",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@types/lodash": "^4.17.13",
"@types/mdast": "^4.0.4",
"@types/node": "^22.10.1",
"typescript": "^5.7.2"
}
}

View File

@@ -0,0 +1 @@
export { MarkdownPlugin } from './markdown.js';

View File

@@ -0,0 +1,71 @@
import { createActionApiRoute, Document, Plugin, z } from '@plaindb/plaindb';
import { MarkdownAst } from './utils/markdown-ast.js';
import { CorePlugin } from '@plaindb/plugin-core';
type MarkdownSubPlugin = {
process: (ast: MarkdownAst) => Promise<void>;
};
class MarkdownPlugin extends Plugin {
#subPlugins = new Set<MarkdownSubPlugin>();
public readonly name = '@builtin/markdown';
public actions = {
register: createActionApiRoute({
input: z.object({
plugin: z.custom<MarkdownSubPlugin>(),
}),
handle: async ({ plugin }) => {
this.#subPlugins.add(plugin);
},
}),
};
public process = async (document: Document) => {
if (!document.location.endsWith('.md')) {
return;
}
const ast = new MarkdownAst(document.data);
for (const plugin of this.#subPlugins) {
await plugin.process(ast);
}
const tags: string[] = [];
let topHead = Number.MAX_SAFE_INTEGER;
let title = 'Untitles';
ast.visit((node) => {
if (node.type !== 'heading') {
return;
}
if (node.depth === undefined) {
return;
}
if (node.depth < topHead) {
topHead = node.depth;
title = ast.nodeToString(node.children);
}
});
await this.action(CorePlugin, 'setTitle', {
title,
document: document.location,
});
ast.visit((node) => {
if (node.type === 'textDirective' && node.name === 'tag') {
const body = ast.nodeToString(node.children);
tags.push(body);
}
});
document.replace(ast.toBuffer());
await this.action(CorePlugin, 'setTags', {
tags,
document: document.location,
});
};
}
export { MarkdownPlugin };

View File

@@ -0,0 +1,76 @@
import { Root, Node as BaseNode } from 'mdast';
import { type PropertyName } from 'lodash';
import get from 'lodash/get.js';
import set from 'lodash/set.js';
import remarkDirective from 'remark-directive';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import { unified } from 'unified';
import { toString } from 'mdast-util-to-string';
type Node = {
children?: Node[];
attributes?: Record<string, unknown>;
name?: string;
depth?: number;
value?: string;
} & BaseNode;
const compiler = unified()
.use(remarkParse)
.use(remarkStringify)
.use(remarkGfm)
.use(remarkFrontmatter, ['yaml'])
.use(remarkDirective)
.use(remarkMath);
class MarkdownAst {
content: Root;
constructor(content: Buffer) {
this.content = this.#parse(content.toString());
}
public set = (path: PropertyName[], value: unknown) => {
set(this.content, path, value);
};
public get = (path: PropertyName[]) => {
return get(this.content, path);
};
#parse = (input: string | null) => {
const ast = compiler.parse(input || '');
return ast;
};
public toBuffer = () => {
const output = compiler.stringify(this.content);
return Buffer.from(output);
};
public visit = (visitor: (node: Node, path: PropertyName[]) => void) => {
const visit = (node: Node, path: PropertyName[]) => {
visitor(node, path);
if (node.children) {
node.children.forEach((child, index) => {
visit(child, [...path, 'children', index]);
});
}
};
visit(this.content, []);
};
public nodeToString = (node: Node[] = []) => {
return toString(node);
};
public patch = (visitor: (node: Node, path: PropertyName[]) => void) => {
this.visit(visitor);
};
}
export { MarkdownAst, type Node };

View File

@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist"
},
"include": [
"src"
]
}