自新世界 0x06:生活在 AST 上

自新世界 0x06:生活在 AST 上

日期:2024/9/21 分类: 周报 页面仔的自我修养 标签: TypeScript AST RemNote A detailed anime-style illustration of a programmer living on an abstract syntax tree (AST), inspired by the art style commonly found on Pixiv. The tree has branches and nodes resembling abstract elements of loops, branches, and other coding structures. The programmer is portrayed as an anime character, sitting on of the branches with a laptop, surrounded by floating abstract code symbols, lines, and shapes representing different coding structures. The background features an abstract, colorful representation of loops and branches in a flowing and dynamic pattern, blending seamlessly with the tree, giving a sense of a digital world merging with the anime aesthetic.

生活在 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: stringuserId: string; knowledgebaseId: stringknowledgebaseId: string; name: stringname: string; exportDate: stringexportDate: string; exportVersion: numberexportVersion: number; documentRemToExportId: stringdocumentRemToExportId: string; docs: Doc[]docs: type Doc = /*unresolved*/ anyDoc[]; }

整个 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) => TreeA **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(): voidremParse() {
  const const self: anyself = this;
 
  const self: anyself.parser = function (local function) parser(doc: string, file: unknown): anyparser as type Parser<Tree extends import("unist").Node = Node> = (document: string, file: VFile) => TreeA **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): anyparser(doc: stringdoc: string, file: unknownfile: unknown) {
    return transformDoc(...);
  }
}
 
let let processor: anyprocessor = 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(): voidremParse).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) KK 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: numbera: number, b: stringb: string, } type

文章来源:

Author:ᡥᠠᡳᡤᡳᠶᠠ ᡥᠠᠯᠠ·ᠨᡝᡴᠣ 猫
link:https://blog.xinshijiededa.men/weekly/0x06/