mirror of
https://github.com/morten-olsen/plainidx.git
synced 2026-02-08 01:06:24 +01:00
init
This commit is contained in:
2
packages/plugin-markdown/.gitignore
vendored
Normal file
2
packages/plugin-markdown/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/node_modules/
|
||||
/dist/
|
||||
34
packages/plugin-markdown/package.json
Normal file
34
packages/plugin-markdown/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
1
packages/plugin-markdown/src/exports.ts
Normal file
1
packages/plugin-markdown/src/exports.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { MarkdownPlugin } from './markdown.js';
|
||||
71
packages/plugin-markdown/src/markdown.ts
Normal file
71
packages/plugin-markdown/src/markdown.ts
Normal 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 };
|
||||
76
packages/plugin-markdown/src/utils/markdown-ast.ts
Normal file
76
packages/plugin-markdown/src/utils/markdown-ast.ts
Normal 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 };
|
||||
9
packages/plugin-markdown/tsconfig.json
Normal file
9
packages/plugin-markdown/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user