自新世界 0x06:生活在 AST 上
自新世界 0x06:生活在 AST 上
日期:2024/9/21 分类: 周报 页面仔的自我修养 标签: TypeScript AST RemNote生活在 AST 上
rehype-remnote#
写了一个可以从 RemNote 导出的 .rem 文件中生成 hast 的 包。hast 是 rehype 所用的抽象语法树,实际写下来感觉还是挺方便的。
.rem 文件结构#
新的 .rem 文件本质是一个 .tar.gz 压缩包(以前则是 .zip),解压后有一个 rem.json 文件,里面包含了所有笔记的内容。应该是为了节省空间,所以这个 JSON 中很多字段的名称都很难以分辨。于是写了一个测试各种样式的页面,导出看效果,根据效果修改代码。
文件的结构大概是这样:
type type Workspace = {
userId: string;
knowledgebaseId: string;
name: string;
exportDate: string;
exportVersion: number;
documentRemToExportId: string;
docs: Doc[];
}
Workspace = {
userId: string
userId: string;
knowledgebaseId: string
knowledgebaseId: string;
name: string
name: string;
exportDate: string
exportDate: string;
exportVersion: number
exportVersion: number;
documentRemToExportId: string
documentRemToExportId: string;
docs: Doc[]
docs: type Doc = /*unresolved*/ any
Doc[];
}
整个 docs 的结构是扁平而有序的,通过 _id 来找到对应的内容。对于单个 Doc
,主要的内容在 .key 中。.key 中存储的内容是 (string|object)[]
。对于一张卡片,如果有涉及到闪卡的部分,可能会把背面的内容放在其他地方。
一些特殊的 block 类型会存储在几个特殊的 block 中,比如
type type RemTypes = "Daily Document" | "Document" | "Automatically Sort" | "Tags" | "Template Slot" | "Header" | "Website" | "Link" | "Quote" | "Image" | "Code" | "Card Item" | "List Item"
RemTypes = "Daily Document"
| "Document"
| "Automatically Sort"
| "Tags"
| "Template Slot"
| "Header"
| "Website"
| "Link"
| "Quote"
| "Image"
| "Code"
| "Card Item"
| "List Item"
如何使用#
可以参考 Rem.astro 文件。
如果你只需要最后的 HTML,那么可以调用rem2Html
。
如果你需要将 rem.json 的内容提交到版本管理系统中,那么建议你先调用 hydrate
来生成一个精简的、树状的 JSON。
如果你想要走完整个 unified
的 pipeline,得有一个 parser 来初始化 unist:
import { const unified: Processor<undefined, undefined, undefined, undefined, undefined>
Create a new processor.@example This example shows how a new processor can be created (from `remark`) and linked
to **stdin**(4) and **stdout**(4).
```js
import process from 'node:process'
import concatStream from 'concat-stream'
import {remark} from 'remark'
process.stdin.pipe(
concatStream(function (buf) {
process.stdout.write(String(remark().processSync(buf)))
})
)
```@returns New *unfrozen* processor (`processor`).
This processor is configured to work the same as its ancestor.
When the descendant processor is configured in the future it does not
affect the ancestral processor.unified, type type Parser<Tree extends import("unist").Node = Node> = (document: string, file: VFile) => Tree
A **parser** handles the parsing of text to a syntax tree.
It is used in the parse phase and is called with a `string` and
{@linkcode
VFile
}
of the document to parse.
It must return the syntax tree representation of the given file
(
{@linkcode
Node
}
).Parser } from "unified";
function function remParse(): void
remParse() {
const const self: any
self = this;
const self: any
self.parser = function (local function) parser(doc: string, file: unknown): any
parser as type Parser<Tree extends import("unist").Node = Node> = (document: string, file: VFile) => Tree
A **parser** handles the parsing of text to a syntax tree.
It is used in the parse phase and is called with a `string` and
{@linkcode
VFile
}
of the document to parse.
It must return the syntax tree representation of the given file
(
{@linkcode
Node
}
).Parser;
function function (local function) parser(doc: string, file: unknown): any
parser(doc: string
doc: string, file: unknown
file: unknown) {
return transformDoc(...);
}
}
let let processor: any
processor = function unified(): Processor<undefined, undefined, undefined, undefined, undefined>
Create a new processor.@example This example shows how a new processor can be created (from `remark`) and linked
to **stdin**(4) and **stdout**(4).
```js
import process from 'node:process'
import concatStream from 'concat-stream'
import {remark} from 'remark'
process.stdin.pipe(
concatStream(function (buf) {
process.stdout.write(String(remark().processSync(buf)))
})
)
```@returns New *unfrozen* processor (`processor`).
This processor is configured to work the same as its ancestor.
When the descendant processor is configured in the future it does not
affect the ancestral processor.unified().
Processor<undefined, undefined, undefined, undefined, undefined>.use<[], undefined, undefined>(plugin: Plugin<[], undefined, undefined>, ...parameters: [] | [boolean]): Processor<undefined, undefined, undefined, undefined, undefined> (+2 overloads)
Configure the processor to use a plugin, a list of usable values, or a
preset.
If the processor is already using a plugin, the previous plugin
configuration is changed based on the options that are passed in.
In other words, the plugin is not added a second time.
> **Note**: `use` cannot be called on *frozen* processors.
> Call the processor first to create a new unfrozen processor.@example There are many ways to pass plugins to `.use()`.
This example gives an overview:
```js
import {unified} from 'unified'
unified()
// Plugin with options:
.use(pluginA, {x: true, y: true})
// Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):
.use(pluginA, {y: false, z: true})
// Plugins:
.use([pluginB, pluginC])
// Two plugins, the second with options:
.use([pluginD, [pluginE, {}]])
// Preset with plugins and settings:
.use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}})
// Settings only:
.use({settings: {position: false}})
```@template{Array<unknown>} [Parameters=[]]@template{Node | string | undefined} [Input=undefined]@template[Output=Input]@overload@overload@overload@paramvalue Usable value.@paramparameters Parameters, when a plugin is given as a usable value.@returnsCurrent processor.use(function remParse(): void
remParse).applyargumentsbindcallcallercompilerdatafreezelengthnameparseparserprocessprocessSyncprototyperunrunSyncstringifytoStringuseattachersCompilercopyfreezeIndexfrozennamespaceParsertransformers
如果你想要直接传 string 的话,可以跳过 process
的步骤,自行调用相应的 stage。
| ........................ process ........................... |
| .......... parse ... | ... run ... | ... stringify ..........|
+--------+ +----------+
Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
+--------+ | +----------+
X
|
+--------------+
| Transformers |
+--------------+
rehype-remnote/style
提供了一些基础的样式,可以导入使用。
TypeScript 类型体操之一个 Record
要么有一组 kv 的全部,要么全部没有#
处理 rem.json 的时候发现很多时候结构体中的部分字段要么同时出现,要么同时不出现,遂有了下面的一些类型体操。你也可以在操场上玩耍。
type type NoneOf<S> = { [K in keyof S]?: undefined; }
NoneOf<function (type parameter) S in type NoneOf<S>
S> = {[function (type parameter) K
K in keyof function (type parameter) S in type NoneOf<S>
S]?: never};
type type AllOrNone<B, S> = (B & S) | (B & NoneOf<S>)
AllOrNone<function (type parameter) B in type AllOrNone<B, S>
B, function (type parameter) S in type AllOrNone<B, S>
S> = function (type parameter) B in type AllOrNone<B, S>
B & function (type parameter) S in type AllOrNone<B, S>
S | function (type parameter) B in type AllOrNone<B, S>
B & type NoneOf<S> = { [K in keyof S]?: undefined; }
NoneOf<function (type parameter) S in type AllOrNone<B, S>
S>;
/////////////
type type Base = {
a: number;
b: string;
}
Base = {
a: number
a: number,
b: string
b: string,
}
type
文章来源:
Author:ᡥᠠᡳᡤᡳᠶᠠ ᡥᠠᠯᠠ·ᠨᡝᡴᠣ 猫
link:https://blog.xinshijiededa.men/weekly/0x06/
下一篇:自新世界 #0x07:巨大的蛋挞 上一篇:编码理论笔记