@cosense-toolbox/parser 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +794 -0
- package/dist/ast-CIKXwSl5.mjs +41 -0
- package/dist/ast-CIKXwSl5.mjs.map +1 -0
- package/dist/ast-uYWtkHwT.cjs +52 -0
- package/dist/ast-uYWtkHwT.cjs.map +1 -0
- package/dist/compile.cjs +265 -0
- package/dist/compile.cjs.map +1 -0
- package/dist/compile.d.cts +135 -0
- package/dist/compile.d.cts.map +1 -0
- package/dist/compile.d.mts +135 -0
- package/dist/compile.d.mts.map +1 -0
- package/dist/compile.mjs +256 -0
- package/dist/compile.mjs.map +1 -0
- package/dist/create-compiler-CvlndKOA.d.cts +23 -0
- package/dist/create-compiler-CvlndKOA.d.cts.map +1 -0
- package/dist/create-compiler-jdA408ny.d.mts +23 -0
- package/dist/create-compiler-jdA408ny.d.mts.map +1 -0
- package/dist/image-url-YcTed8tg.cjs +68 -0
- package/dist/image-url-YcTed8tg.cjs.map +1 -0
- package/dist/image-url-eQ6X-oN0.mjs +51 -0
- package/dist/image-url-eQ6X-oN0.mjs.map +1 -0
- package/dist/index.cjs +716 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +77 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +709 -0
- package/dist/index.mjs.map +1 -0
- package/dist/plugin.cjs +0 -0
- package/dist/plugin.d.cts +4 -0
- package/dist/plugin.d.mts +4 -0
- package/dist/plugin.mjs +1 -0
- package/dist/schema.cjs +171 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +34 -0
- package/dist/schema.d.cts.map +1 -0
- package/dist/schema.d.mts +34 -0
- package/dist/schema.d.mts.map +1 -0
- package/dist/schema.mjs +148 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/types-BXlnUFr0.d.mts +63 -0
- package/dist/types-BXlnUFr0.d.mts.map +1 -0
- package/dist/types-Bo_BrmvK.d.cts +230 -0
- package/dist/types-Bo_BrmvK.d.cts.map +1 -0
- package/dist/types-Bo_BrmvK.d.mts +230 -0
- package/dist/types-Bo_BrmvK.d.mts.map +1 -0
- package/dist/types-DVwlUtla.d.cts +63 -0
- package/dist/types-DVwlUtla.d.cts.map +1 -0
- package/dist/utils.cjs +80 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +46 -0
- package/dist/utils.d.cts.map +1 -0
- package/dist/utils.d.mts +46 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +72 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/ast.ts
|
|
2
|
+
/** 型を知らないノードの子。mdast と同じく `children` 配列があればそれが子。 */
|
|
3
|
+
const unknownChildren = (node) => {
|
|
4
|
+
const children = node.children;
|
|
5
|
+
return Array.isArray(children) ? children : [];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* ノードの子を返す。子を持たないノードでは空配列。
|
|
9
|
+
*
|
|
10
|
+
* どのフィールドが子にあたるかはノード型ごとに違う (`children` / `lines` / `rows` / `cells`)。
|
|
11
|
+
* その対応を知っているのはここだけで、AST を辿るコードはすべてこれを通る。
|
|
12
|
+
*/
|
|
13
|
+
const childrenOf = (node) => {
|
|
14
|
+
switch (node.type) {
|
|
15
|
+
case "page": return node.children;
|
|
16
|
+
case "title": return node.children;
|
|
17
|
+
case "codeBlock": return node.lines;
|
|
18
|
+
case "table": return node.rows;
|
|
19
|
+
case "tableRow": return node.cells;
|
|
20
|
+
case "line": return node.children;
|
|
21
|
+
case "decoration": return node.children;
|
|
22
|
+
case "codeLine":
|
|
23
|
+
case "tableCell":
|
|
24
|
+
case "text":
|
|
25
|
+
case "internalLink":
|
|
26
|
+
case "externalLink":
|
|
27
|
+
case "projectLink":
|
|
28
|
+
case "hashtag":
|
|
29
|
+
case "inlineCode":
|
|
30
|
+
case "image":
|
|
31
|
+
case "icon":
|
|
32
|
+
case "formula": return [];
|
|
33
|
+
default: return unknownChildren(node);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/** そのノードがソース上で占めていた生のテキスト。位置情報から復元する。 */
|
|
37
|
+
const rawTextOf = (source, node) => source.slice(node.position.start.offset, node.position.end.offset);
|
|
38
|
+
//#endregion
|
|
39
|
+
export { rawTextOf as n, childrenOf as t };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=ast-CIKXwSl5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-CIKXwSl5.mjs","names":[],"sources":["../src/ast.ts"],"sourcesContent":["/**\n * ast.ts — AST に対する基本操作。パースはしない。\n */\nimport type { AnyNode } from './types'\n\n/** 型を知らないノードの子。mdast と同じく `children` 配列があればそれが子。 */\nconst unknownChildren = (node: unknown): readonly AnyNode[] => {\n const children = (node as { readonly children?: unknown }).children\n return Array.isArray(children) ? (children as readonly AnyNode[]) : []\n}\n\n/**\n * ノードの子を返す。子を持たないノードでは空配列。\n *\n * どのフィールドが子にあたるかはノード型ごとに違う (`children` / `lines` / `rows` / `cells`)。\n * その対応を知っているのはここだけで、AST を辿るコードはすべてこれを通る。\n */\nexport const childrenOf = (node: AnyNode): readonly AnyNode[] => {\n switch (node.type) {\n case 'page':\n return node.children\n case 'title':\n return node.children\n case 'codeBlock':\n return node.lines\n case 'table':\n return node.rows\n case 'tableRow':\n return node.cells\n case 'line':\n return node.children\n case 'decoration':\n return node.children\n case 'codeLine':\n case 'tableCell':\n case 'text':\n case 'internalLink':\n case 'externalLink':\n case 'projectLink':\n case 'hashtag':\n case 'inlineCode':\n case 'image':\n case 'icon':\n case 'formula':\n return []\n default: {\n // ノード型を足したのにここを更新していないとコンパイルエラーになる。\n node satisfies never\n // 型の上では到達しないが、実行時には declaration merging でノード型を足した\n // プラグインのノードが来うる。\n return unknownChildren(node)\n }\n }\n}\n\n/** そのノードがソース上で占めていた生のテキスト。位置情報から復元する。 */\nexport const rawTextOf = (source: string, node: AnyNode): string =>\n source.slice(node.position.start.offset, node.position.end.offset)\n"],"mappings":";;AAMA,MAAM,mBAAmB,SAAsC;CAC7D,MAAM,WAAY,KAAyC;CAC3D,OAAO,MAAM,QAAQ,QAAQ,IAAK,WAAkC,CAAC;AACvE;;;;;;;AAQA,MAAa,cAAc,SAAsC;CAC/D,QAAQ,KAAK,MAAb;EACE,KAAK,QACH,OAAO,KAAK;EACd,KAAK,SACH,OAAO,KAAK;EACd,KAAK,aACH,OAAO,KAAK;EACd,KAAK,SACH,OAAO,KAAK;EACd,KAAK,YACH,OAAO,KAAK;EACd,KAAK,QACH,OAAO,KAAK;EACd,KAAK,cACH,OAAO,KAAK;EACd,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,WACH,OAAO,CAAC;EACV,SAKE,OAAO,gBAAgB,IAAI;CAE/B;AACF;;AAGA,MAAa,aAAa,QAAgB,SACxC,OAAO,MAAM,KAAK,SAAS,MAAM,QAAQ,KAAK,SAAS,IAAI,MAAM"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/ast.ts
|
|
2
|
+
/** 型を知らないノードの子。mdast と同じく `children` 配列があればそれが子。 */
|
|
3
|
+
const unknownChildren = (node) => {
|
|
4
|
+
const children = node.children;
|
|
5
|
+
return Array.isArray(children) ? children : [];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* ノードの子を返す。子を持たないノードでは空配列。
|
|
9
|
+
*
|
|
10
|
+
* どのフィールドが子にあたるかはノード型ごとに違う (`children` / `lines` / `rows` / `cells`)。
|
|
11
|
+
* その対応を知っているのはここだけで、AST を辿るコードはすべてこれを通る。
|
|
12
|
+
*/
|
|
13
|
+
const childrenOf = (node) => {
|
|
14
|
+
switch (node.type) {
|
|
15
|
+
case "page": return node.children;
|
|
16
|
+
case "title": return node.children;
|
|
17
|
+
case "codeBlock": return node.lines;
|
|
18
|
+
case "table": return node.rows;
|
|
19
|
+
case "tableRow": return node.cells;
|
|
20
|
+
case "line": return node.children;
|
|
21
|
+
case "decoration": return node.children;
|
|
22
|
+
case "codeLine":
|
|
23
|
+
case "tableCell":
|
|
24
|
+
case "text":
|
|
25
|
+
case "internalLink":
|
|
26
|
+
case "externalLink":
|
|
27
|
+
case "projectLink":
|
|
28
|
+
case "hashtag":
|
|
29
|
+
case "inlineCode":
|
|
30
|
+
case "image":
|
|
31
|
+
case "icon":
|
|
32
|
+
case "formula": return [];
|
|
33
|
+
default: return unknownChildren(node);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/** そのノードがソース上で占めていた生のテキスト。位置情報から復元する。 */
|
|
37
|
+
const rawTextOf = (source, node) => source.slice(node.position.start.offset, node.position.end.offset);
|
|
38
|
+
//#endregion
|
|
39
|
+
Object.defineProperty(exports, "childrenOf", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function() {
|
|
42
|
+
return childrenOf;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(exports, "rawTextOf", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
get: function() {
|
|
48
|
+
return rawTextOf;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=ast-uYWtkHwT.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-uYWtkHwT.cjs","names":[],"sources":["../src/ast.ts"],"sourcesContent":["/**\n * ast.ts — AST に対する基本操作。パースはしない。\n */\nimport type { AnyNode } from './types'\n\n/** 型を知らないノードの子。mdast と同じく `children` 配列があればそれが子。 */\nconst unknownChildren = (node: unknown): readonly AnyNode[] => {\n const children = (node as { readonly children?: unknown }).children\n return Array.isArray(children) ? (children as readonly AnyNode[]) : []\n}\n\n/**\n * ノードの子を返す。子を持たないノードでは空配列。\n *\n * どのフィールドが子にあたるかはノード型ごとに違う (`children` / `lines` / `rows` / `cells`)。\n * その対応を知っているのはここだけで、AST を辿るコードはすべてこれを通る。\n */\nexport const childrenOf = (node: AnyNode): readonly AnyNode[] => {\n switch (node.type) {\n case 'page':\n return node.children\n case 'title':\n return node.children\n case 'codeBlock':\n return node.lines\n case 'table':\n return node.rows\n case 'tableRow':\n return node.cells\n case 'line':\n return node.children\n case 'decoration':\n return node.children\n case 'codeLine':\n case 'tableCell':\n case 'text':\n case 'internalLink':\n case 'externalLink':\n case 'projectLink':\n case 'hashtag':\n case 'inlineCode':\n case 'image':\n case 'icon':\n case 'formula':\n return []\n default: {\n // ノード型を足したのにここを更新していないとコンパイルエラーになる。\n node satisfies never\n // 型の上では到達しないが、実行時には declaration merging でノード型を足した\n // プラグインのノードが来うる。\n return unknownChildren(node)\n }\n }\n}\n\n/** そのノードがソース上で占めていた生のテキスト。位置情報から復元する。 */\nexport const rawTextOf = (source: string, node: AnyNode): string =>\n source.slice(node.position.start.offset, node.position.end.offset)\n"],"mappings":";;AAMA,MAAM,mBAAmB,SAAsC;CAC7D,MAAM,WAAY,KAAyC;CAC3D,OAAO,MAAM,QAAQ,QAAQ,IAAK,WAAkC,CAAC;AACvE;;;;;;;AAQA,MAAa,cAAc,SAAsC;CAC/D,QAAQ,KAAK,MAAb;EACE,KAAK,QACH,OAAO,KAAK;EACd,KAAK,SACH,OAAO,KAAK;EACd,KAAK,aACH,OAAO,KAAK;EACd,KAAK,SACH,OAAO,KAAK;EACd,KAAK,YACH,OAAO,KAAK;EACd,KAAK,QACH,OAAO,KAAK;EACd,KAAK,cACH,OAAO,KAAK;EACd,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,WACH,OAAO,CAAC;EACV,SAKE,OAAO,gBAAgB,IAAI;CAE/B;AACF;;AAGA,MAAa,aAAa,QAAgB,SACxC,OAAO,MAAM,KAAK,SAAS,MAAM,QAAQ,KAAK,SAAS,IAAI,MAAM"}
|
package/dist/compile.cjs
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_image_url = require("./image-url-YcTed8tg.cjs");
|
|
3
|
+
const require_ast = require("./ast-uYWtkHwT.cjs");
|
|
4
|
+
let effect = require("effect");
|
|
5
|
+
//#region src/compile/create-compiler.ts
|
|
6
|
+
/**
|
|
7
|
+
* create-compiler.ts — AST を任意の形式に変換する土台。
|
|
8
|
+
* ハンドラの無いノード型は fallback に回るので、一部だけ差し替えれば済む。
|
|
9
|
+
*/
|
|
10
|
+
/** AST をひとつの値に変換する関数を作る。 */
|
|
11
|
+
const createCompiler = (options) => {
|
|
12
|
+
const compile = (node) => {
|
|
13
|
+
return (options.handlers[node.type] ?? options.fallback)(node, ctx);
|
|
14
|
+
};
|
|
15
|
+
const ctx = {
|
|
16
|
+
node: compile,
|
|
17
|
+
children: (node) => require_ast.childrenOf(node).map(compile)
|
|
18
|
+
};
|
|
19
|
+
return compile;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/compile/to-html.ts
|
|
23
|
+
/**
|
|
24
|
+
* to-html.ts — AST を HTML にする公式コンパイラ。
|
|
25
|
+
*
|
|
26
|
+
* ノード型ごとのハンドラの集まりで、`handlers` に渡した型だけが差し替わる
|
|
27
|
+
* (marked の renderer と同じ形)。
|
|
28
|
+
*/
|
|
29
|
+
const HTML_ESCAPES = {
|
|
30
|
+
"&": "&",
|
|
31
|
+
"<": "<",
|
|
32
|
+
">": ">",
|
|
33
|
+
"\"": """,
|
|
34
|
+
"'": "'"
|
|
35
|
+
};
|
|
36
|
+
/** `& < > " '` を実体参照に置き換える。テキストと属性値のどちらにも使える。 */
|
|
37
|
+
const escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
|
|
38
|
+
/** 属性ひとつ。None なら属性ごと出さない。 */
|
|
39
|
+
const attr = (name, value) => effect.Option.match(value, {
|
|
40
|
+
onNone: () => "",
|
|
41
|
+
onSome: (text) => ` ${name}="${escapeHtml(text)}"`
|
|
42
|
+
});
|
|
43
|
+
/** 空文字は「指定なし」とみなす。class 名を空にして属性ごと消せるようにするため。 */
|
|
44
|
+
const nonEmpty = (value) => (0, effect.pipe)(effect.Option.fromNullable(value), effect.Option.filter(effect.String.isNonEmpty));
|
|
45
|
+
/** 0 は既定値なので属性にする意味がない。 */
|
|
46
|
+
const positive = (value) => value > 0 ? effect.Option.some(String(value)) : effect.Option.none();
|
|
47
|
+
const flag = (on) => on === true ? effect.Option.some("true") : effect.Option.none();
|
|
48
|
+
/** `href` に入れると script が動くスキーム。`data:text/html` があるので data: も拒む。 */
|
|
49
|
+
const UNSAFE_HREF_RE = /^(?:javascript|vbscript|data):/;
|
|
50
|
+
/** `src` に入れると script が動くスキーム。`data:` 画像は正当な使い道があるので許す。 */
|
|
51
|
+
const UNSAFE_SRC_RE = /^(?:javascript|vbscript):/;
|
|
52
|
+
/**
|
|
53
|
+
* スキームだけを見るために空白と制御文字を落とす。
|
|
54
|
+
* ブラウザは途中にタブや改行が挟まった `javascript:` もスキームとして解釈するため。
|
|
55
|
+
*/
|
|
56
|
+
const schemeOf = (url) => url.replace(/[\s\p{Cc}]/gu, "").toLowerCase();
|
|
57
|
+
const safeUrl = (url, unsafe) => unsafe.test(schemeOf(url)) ? null : url;
|
|
58
|
+
/** `href` に入れて安全な URL だけを返す。script が動くスキームなら null。 */
|
|
59
|
+
const safeHref = (url) => safeUrl(url, UNSAFE_HREF_RE);
|
|
60
|
+
/** `src` に入れて安全な URL だけを返す。script が動くスキームなら null。 */
|
|
61
|
+
const safeSrc = (url) => safeUrl(url, UNSAFE_SRC_RE);
|
|
62
|
+
/** 遷移先があるときだけ `<a>` で包む。スキームが安全でなければ包まない。 */
|
|
63
|
+
const linked = (html, link) => (0, effect.pipe)(effect.Option.fromNullable(link), effect.Option.flatMap((url) => nonEmpty(safeHref(url))), effect.Option.match({
|
|
64
|
+
onNone: () => html,
|
|
65
|
+
onSome: (href) => `<a${attr("href", effect.Option.some(href))}>${html}</a>`
|
|
66
|
+
}));
|
|
67
|
+
/** 既定の class 名。接頭辞を持たないため、利用側の CSS と名前がぶつかりうる。 */
|
|
68
|
+
const defaultClassNames = {
|
|
69
|
+
page: "page",
|
|
70
|
+
title: "title",
|
|
71
|
+
line: "line",
|
|
72
|
+
quote: "quote",
|
|
73
|
+
monospace: "monospace",
|
|
74
|
+
codeBlock: "code-block",
|
|
75
|
+
codeStart: "code-start",
|
|
76
|
+
codeFilename: "code-block-start",
|
|
77
|
+
codeBody: "code-body",
|
|
78
|
+
codeHighlight: "highlight",
|
|
79
|
+
table: "table",
|
|
80
|
+
indentMark: "indent-mark",
|
|
81
|
+
pad: "pad",
|
|
82
|
+
dot: "dot",
|
|
83
|
+
internalLink: "link",
|
|
84
|
+
externalLink: "link link-external",
|
|
85
|
+
projectLink: "link link-project",
|
|
86
|
+
hashtag: "hashtag",
|
|
87
|
+
inlineCode: "code",
|
|
88
|
+
image: "image",
|
|
89
|
+
icon: "icon",
|
|
90
|
+
formula: "formula",
|
|
91
|
+
decoration: "decoration"
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* ファイル名から言語名を推測する。拡張子があればそれ、無ければファイル名全体。
|
|
95
|
+
* Cosense では `code:python` のように言語名だけを書くこともできるため。
|
|
96
|
+
*/
|
|
97
|
+
const languageOf = (filename) => {
|
|
98
|
+
const dot = filename.lastIndexOf(".");
|
|
99
|
+
return (dot > 0 ? filename.slice(dot + 1) : filename).toLowerCase();
|
|
100
|
+
};
|
|
101
|
+
/** ハイライタに渡すコード本体。 */
|
|
102
|
+
const rawCodeOf = (node) => node.lines.map((codeLine) => codeLine.value).join("\n");
|
|
103
|
+
/** 記法に書かれたページタイトル。ノード型ごとに置き場所が違うのをここで吸収する。 */
|
|
104
|
+
const pageTitleOf = (node) => effect.Match.value(node).pipe(effect.Match.when({ type: "hashtag" }, (tag) => tag.value), effect.Match.when({ type: "icon" }, (icon) => icon.user), effect.Match.orElse((link) => link.target));
|
|
105
|
+
/**
|
|
106
|
+
* `pageUrl` の既定の実装。`/proj/page` のように区切りを含むタイトルは、
|
|
107
|
+
* 区切りを残したまま各段を encode する。
|
|
108
|
+
*/
|
|
109
|
+
const defaultPageUrl = (title) => title.startsWith("/") ? title.split("/").map(encodeURIComponent).join("/") : `/${encodeURIComponent(title)}`;
|
|
110
|
+
/**
|
|
111
|
+
* 装飾を表す要素を、内側から外側の順に並べたもの。
|
|
112
|
+
*
|
|
113
|
+
* Cosense では 1 つの記法が複数の装飾を同時に持つ (`[-/ x]` は打消しかつ斜体) ので、
|
|
114
|
+
* フラグの集合を入れ子の要素に開いて表現する。
|
|
115
|
+
*/
|
|
116
|
+
const DECORATION_TAGS = [
|
|
117
|
+
[(node) => node.strike, "s"],
|
|
118
|
+
[(node) => node.underline, "u"],
|
|
119
|
+
[(node) => node.italic, "em"],
|
|
120
|
+
[(node) => node.bold, "strong"]
|
|
121
|
+
];
|
|
122
|
+
const wrapDecoration = (node, inner) => DECORATION_TAGS.filter(([isOn]) => isOn(node)).reduce((html, [, tag]) => `<${tag}>${html}</${tag}>`, inner);
|
|
123
|
+
/**
|
|
124
|
+
* 既定のハンドラ一式。`toHtml` はこれに `handlers` を重ねてから走らせるので、
|
|
125
|
+
* 一部だけ差し替えるのに呼ぶ必要はない。既定の出力を包みたいときに使う。
|
|
126
|
+
*/
|
|
127
|
+
const createHtmlHandlers = (options = {}) => {
|
|
128
|
+
const cls = {
|
|
129
|
+
...defaultClassNames,
|
|
130
|
+
...options.classNames
|
|
131
|
+
};
|
|
132
|
+
const highlight = options.highlight;
|
|
133
|
+
const pageUrl = options.pageUrl ?? defaultPageUrl;
|
|
134
|
+
const iconImageUrl = options.iconImageUrl ?? (() => null);
|
|
135
|
+
const pageHref = (node) => nonEmpty(safeHref(pageUrl(pageTitleOf(node), node)));
|
|
136
|
+
/** 余白の数がインデントの深さを表し、中点はその右端に付く (本家と同じ形)。 */
|
|
137
|
+
const indentMark = (indent) => {
|
|
138
|
+
const pad = `<span${attr("class", nonEmpty(cls.pad))}> </span>`;
|
|
139
|
+
const dot = `<span${attr("class", nonEmpty(cls.dot))}></span>`;
|
|
140
|
+
return `<span${attr("class", nonEmpty(cls.indentMark))}>${pad.repeat(indent)}${dot}</span>`;
|
|
141
|
+
};
|
|
142
|
+
const anchor = (url, className, label) => {
|
|
143
|
+
return `${`<a${attr("class", nonEmpty(className))}${attr("href", url)}>`}${escapeHtml(label)}</a>`;
|
|
144
|
+
};
|
|
145
|
+
return {
|
|
146
|
+
page: (node, ctx) => `<div${attr("class", nonEmpty(cls.page))}>${ctx.children(node).join("")}</div>`,
|
|
147
|
+
title: (node, ctx) => `<h1${attr("class", nonEmpty(cls.title))}>${ctx.children(node).join("")}</h1>`,
|
|
148
|
+
line: (node, ctx) => {
|
|
149
|
+
const body = ctx.children(node).join("");
|
|
150
|
+
const styled = node.monospace ? `<code${attr("class", nonEmpty(cls.monospace))}>${body}</code>` : body;
|
|
151
|
+
const quoted = node.quote ? `<blockquote${attr("class", nonEmpty(cls.quote))}>${styled}</blockquote>` : styled;
|
|
152
|
+
const inner = quoted === "" ? "<br>" : quoted;
|
|
153
|
+
const mark = options.showPads === true && node.indent > 0 ? indentMark(node.indent) : "";
|
|
154
|
+
return `${`<div${attr("class", nonEmpty(cls.line))}${attr("data-indent", positive(node.indent))}>`}${mark}${inner}</div>`;
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* 1 行 = 1 要素に切る (本家と同じ)。行ごとにインデントを付けられるようにするため。
|
|
158
|
+
* `highlight` があるときだけ、複数行にまたがるタグを壊さないよう本体をまとめる。
|
|
159
|
+
*/
|
|
160
|
+
codeBlock: (node, ctx) => {
|
|
161
|
+
const classes = [cls.line, cls.codeBlock].filter((name) => name !== void 0).join(" ");
|
|
162
|
+
const blockLine = (indent, inner) => `<div${attr("class", nonEmpty(classes))}${attr("data-indent", positive(indent))}>${inner}</div>`;
|
|
163
|
+
const codeBody = (className, inner) => blockLine(node.indent + 1, `<code${attr("class", nonEmpty(className))}>${inner}</code>`);
|
|
164
|
+
const filename = `<span${attr("class", nonEmpty(cls.codeFilename))}>${escapeHtml(node.filename)}</span>`;
|
|
165
|
+
const header = blockLine(node.indent, `<code${attr("class", nonEmpty(cls.codeStart))}>${filename}</code>`);
|
|
166
|
+
if (highlight !== void 0) return header + codeBody([cls.codeBody, cls.codeHighlight].filter((name) => name !== void 0).join(" "), highlight(rawCodeOf(node), languageOf(node.filename)));
|
|
167
|
+
return header + ctx.children(node).map((value) => codeBody(cls.codeBody, value)).join("");
|
|
168
|
+
},
|
|
169
|
+
codeLine: (node) => escapeHtml(node.value),
|
|
170
|
+
table: (node, ctx) => {
|
|
171
|
+
const caption = (0, effect.pipe)(nonEmpty(node.name), effect.Option.match({
|
|
172
|
+
onNone: () => "",
|
|
173
|
+
onSome: (name) => `<caption>${escapeHtml(name)}</caption>`
|
|
174
|
+
}));
|
|
175
|
+
const rows = ctx.children(node).join("");
|
|
176
|
+
return `<table${attr("class", nonEmpty(cls.table))}>${caption}<tbody>${rows}</tbody></table>`;
|
|
177
|
+
},
|
|
178
|
+
tableRow: (node, ctx) => `<tr>${ctx.children(node).join("")}</tr>`,
|
|
179
|
+
tableCell: (node) => `<td>${escapeHtml(node.value)}</td>`,
|
|
180
|
+
text: (node) => escapeHtml(node.value),
|
|
181
|
+
internalLink: (node) => anchor(pageHref(node), cls.internalLink, node.label),
|
|
182
|
+
externalLink: (node) => anchor(nonEmpty(safeHref(node.target)), cls.externalLink, node.label),
|
|
183
|
+
projectLink: (node) => anchor(pageHref(node), cls.projectLink, node.label),
|
|
184
|
+
hashtag: (node) => anchor(pageHref(node), cls.hashtag, `#${node.value}`),
|
|
185
|
+
inlineCode: (node) => `<code${attr("class", nonEmpty(cls.inlineCode))}>${escapeHtml(node.value)}</code>`,
|
|
186
|
+
image: (node) => {
|
|
187
|
+
const src = require_image_url.asImageSrc(node.src) ?? node.src;
|
|
188
|
+
const open = `<img${attr("class", nonEmpty(cls.image))}${attr("src", nonEmpty(safeSrc(src)))}`;
|
|
189
|
+
return linked(`${open} alt=""${attr("data-large", flag(node.large))}>`, node.link);
|
|
190
|
+
},
|
|
191
|
+
icon: (node) => {
|
|
192
|
+
const name = escapeHtml(node.user);
|
|
193
|
+
const src = iconImageUrl(node);
|
|
194
|
+
const body = src === null ? name : `<img${attr("class", nonEmpty(cls.icon))}${attr("src", nonEmpty(safeSrc(src)))} alt="${name}" title="${name}">`;
|
|
195
|
+
const className = [cls.internalLink, cls.icon].filter((c) => c !== void 0).join(" ");
|
|
196
|
+
return `${`<a${attr("class", nonEmpty(className))}${attr("href", pageHref(node))}>`}${body}</a>`.repeat(node.count);
|
|
197
|
+
},
|
|
198
|
+
formula: (node) => `<span${attr("class", nonEmpty(cls.formula))}>${escapeHtml(node.value)}</span>`,
|
|
199
|
+
decoration: (node, ctx) => {
|
|
200
|
+
return `${`<span${attr("class", nonEmpty(cls.decoration))}${attr("data-size-level", positive(node.sizeLevel))}>`}${wrapDecoration(node, ctx.children(node).join(""))}</span>`;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* ページ (または任意のノード) を HTML 文字列にする。
|
|
206
|
+
*
|
|
207
|
+
* テキストと属性値はエスケープし、`javascript:` のようなスキームの URL は属性ごと落とす。
|
|
208
|
+
* `handlers` と `highlight` が返した文字列はそのまま埋め込むので、
|
|
209
|
+
* そこでのエスケープは書いた人の責任になる。
|
|
210
|
+
*/
|
|
211
|
+
const toHtml = (node, options = {}) => {
|
|
212
|
+
const handlers = {
|
|
213
|
+
...createHtmlHandlers(options),
|
|
214
|
+
...options.handlers
|
|
215
|
+
};
|
|
216
|
+
const html = createCompiler({
|
|
217
|
+
handlers,
|
|
218
|
+
fallback: (other, ctx) => ctx.children(other).join("")
|
|
219
|
+
})(node);
|
|
220
|
+
return options.style === void 0 ? html : `<style>${options.style}</style>${html}`;
|
|
221
|
+
};
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/compile/to-plain-text.ts
|
|
224
|
+
const INDENT_UNIT = " ";
|
|
225
|
+
const compile = createCompiler({
|
|
226
|
+
handlers: {
|
|
227
|
+
page: (node, ctx) => ctx.children(node).join("\n"),
|
|
228
|
+
title: (node) => node.value,
|
|
229
|
+
line: (node, ctx) => {
|
|
230
|
+
const body = ctx.children(node).join("");
|
|
231
|
+
const quote = node.quote ? "> " : "";
|
|
232
|
+
return INDENT_UNIT.repeat(node.indent) + quote + body;
|
|
233
|
+
},
|
|
234
|
+
codeBlock: (node, ctx) => [`${INDENT_UNIT.repeat(node.indent)}${node.filename}`, ...ctx.children(node)].join("\n"),
|
|
235
|
+
codeLine: (node) => node.value,
|
|
236
|
+
table: (node, ctx) => [node.name, ...ctx.children(node)].join("\n"),
|
|
237
|
+
tableRow: (node, ctx) => ctx.children(node).join(" "),
|
|
238
|
+
tableCell: (node) => node.value,
|
|
239
|
+
text: (node) => node.value,
|
|
240
|
+
internalLink: (node) => node.label,
|
|
241
|
+
externalLink: (node) => node.label,
|
|
242
|
+
projectLink: (node) => node.label,
|
|
243
|
+
hashtag: (node) => node.value,
|
|
244
|
+
inlineCode: (node) => node.value,
|
|
245
|
+
formula: (node) => node.value,
|
|
246
|
+
icon: (node) => node.user,
|
|
247
|
+
image: (node) => node.src,
|
|
248
|
+
decoration: (node, ctx) => ctx.children(node).join("")
|
|
249
|
+
},
|
|
250
|
+
fallback: (node, ctx) => ctx.children(node).join("")
|
|
251
|
+
});
|
|
252
|
+
/** ページ (または任意のノード) を記法抜きのテキストにする。 */
|
|
253
|
+
const toPlainText = (node) => compile(node);
|
|
254
|
+
//#endregion
|
|
255
|
+
exports.createCompiler = createCompiler;
|
|
256
|
+
exports.createHtmlHandlers = createHtmlHandlers;
|
|
257
|
+
exports.defaultClassNames = defaultClassNames;
|
|
258
|
+
exports.defaultPageUrl = defaultPageUrl;
|
|
259
|
+
exports.escapeHtml = escapeHtml;
|
|
260
|
+
exports.safeHref = safeHref;
|
|
261
|
+
exports.safeSrc = safeSrc;
|
|
262
|
+
exports.toHtml = toHtml;
|
|
263
|
+
exports.toPlainText = toPlainText;
|
|
264
|
+
|
|
265
|
+
//# sourceMappingURL=compile.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.cjs","names":["childrenOf","Option","pipe","Str","Match","asImageSrc"],"sources":["../src/compile/create-compiler.ts","../src/compile/to-html.ts","../src/compile/to-plain-text.ts"],"sourcesContent":["/**\n * create-compiler.ts — AST を任意の形式に変換する土台。\n * ハンドラの無いノード型は fallback に回るので、一部だけ差し替えれば済む。\n */\nimport { childrenOf } from '../ast'\nimport type { AnyNode, AnyNodeType, NodeOfType } from '../types'\n\n/** ハンドラの中から再帰的に変換するための入口。 */\nexport interface CompileContext<Out> {\n readonly node: (node: AnyNode) => Out\n readonly children: (node: AnyNode) => Out[]\n}\n\nexport type NodeHandler<Out, K extends AnyNodeType> = (\n node: NodeOfType<K>,\n ctx: CompileContext<Out>,\n) => Out\n\n/**\n * ノード型 → ハンドラの対応。`AnyNodeMap` から型を導出しているので、\n * ノード型が増えたらここも自動で新しいキーを受け入れる。\n */\nexport type NodeHandlers<Out> = {\n readonly [K in AnyNodeType]?: NodeHandler<Out, K>\n}\n\nexport interface CompilerOptions<Out> {\n readonly handlers: NodeHandlers<Out>\n /** ハンドラが無いノード型に使う既定の変換 */\n readonly fallback: NodeHandler<Out, AnyNodeType>\n}\n\n/** AST をひとつの値に変換する関数を作る。 */\nexport const createCompiler = <Out>(options: CompilerOptions<Out>) => {\n const compile = (node: AnyNode): Out => {\n // ハンドラの型はキーごとに異なるが、node.type でキーを引いている以上一致する。\n const handler = options.handlers[node.type] as NodeHandler<Out, AnyNodeType> | undefined\n return (handler ?? options.fallback)(node, ctx)\n }\n const ctx: CompileContext<Out> = {\n node: compile,\n children: (node) => childrenOf(node).map(compile),\n }\n return compile\n}\n","/**\n * to-html.ts — AST を HTML にする公式コンパイラ。\n *\n * ノード型ごとのハンドラの集まりで、`handlers` に渡した型だけが差し替わる\n * (marked の renderer と同じ形)。\n */\nimport { Match, Option, String as Str, pipe } from 'effect'\nimport { asImageSrc } from '../core/image-url'\nimport type {\n AnyNode,\n CodeBlock,\n Decoration,\n Hashtag,\n IconNode,\n InternalLink,\n ProjectLink,\n} from '../types'\nimport { type NodeHandlers, createCompiler } from './create-compiler'\n\n// ---------------------------------------------------------------------------\n// エスケープと属性\n// ---------------------------------------------------------------------------\n\nconst HTML_ESCAPES: Readonly<Record<string, string>> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n}\n\n/** `& < > \" '` を実体参照に置き換える。テキストと属性値のどちらにも使える。 */\nexport const escapeHtml = (value: string): string =>\n value.replace(/[&<>\"']/g, (char) => HTML_ESCAPES[char] ?? char)\n\n/** 属性ひとつ。None なら属性ごと出さない。 */\nconst attr = (name: string, value: Option.Option<string>): string =>\n Option.match(value, {\n onNone: () => '',\n onSome: (text) => ` ${name}=\"${escapeHtml(text)}\"`,\n })\n\n/** 空文字は「指定なし」とみなす。class 名を空にして属性ごと消せるようにするため。 */\nconst nonEmpty = (value: string | null | undefined): Option.Option<string> =>\n pipe(Option.fromNullable(value), Option.filter(Str.isNonEmpty))\n\n/** 0 は既定値なので属性にする意味がない。 */\nconst positive = (value: number): Option.Option<string> =>\n value > 0 ? Option.some(String(value)) : Option.none()\n\nconst flag = (on: boolean | undefined): Option.Option<string> =>\n on === true ? Option.some('true') : Option.none()\n\n/** `href` に入れると script が動くスキーム。`data:text/html` があるので data: も拒む。 */\nconst UNSAFE_HREF_RE = /^(?:javascript|vbscript|data):/\n\n/** `src` に入れると script が動くスキーム。`data:` 画像は正当な使い道があるので許す。 */\nconst UNSAFE_SRC_RE = /^(?:javascript|vbscript):/\n\n/**\n * スキームだけを見るために空白と制御文字を落とす。\n * ブラウザは途中にタブや改行が挟まった `javascript:` もスキームとして解釈するため。\n */\nconst schemeOf = (url: string): string => url.replace(/[\\s\\p{Cc}]/gu, '').toLowerCase()\n\nconst safeUrl = (url: string, unsafe: RegExp): string | null =>\n unsafe.test(schemeOf(url)) ? null : url\n\n/** `href` に入れて安全な URL だけを返す。script が動くスキームなら null。 */\nexport const safeHref = (url: string): string | null => safeUrl(url, UNSAFE_HREF_RE)\n\n/** `src` に入れて安全な URL だけを返す。script が動くスキームなら null。 */\nexport const safeSrc = (url: string): string | null => safeUrl(url, UNSAFE_SRC_RE)\n\n/** 遷移先があるときだけ `<a>` で包む。スキームが安全でなければ包まない。 */\nconst linked = (html: string, link: string | undefined): string =>\n pipe(\n Option.fromNullable(link),\n Option.flatMap((url) => nonEmpty(safeHref(url))),\n Option.match({\n onNone: () => html,\n onSome: (href) => `<a${attr('href', Option.some(href))}>${html}</a>`,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// オプション\n// ---------------------------------------------------------------------------\n\n/** プロジェクト内のページを指すノード。本家ではアイコンもユーザーのページへのリンクになる。 */\nexport type PageRefNode = InternalLink | ProjectLink | Hashtag | IconNode\n\n/**\n * 出力する要素に付ける class 名。指定したキーだけが既定を上書きする。\n * 空文字にすると class 属性自体を出さない。\n */\nexport interface HtmlClassNames {\n readonly page?: string\n readonly title?: string\n readonly line?: string\n /** 引用行の `<blockquote>` */\n readonly quote?: string\n /** 等幅行の `<code>` */\n readonly monospace?: string\n /** コードブロックに属する行 (ヘッダ行と本体行の両方) */\n readonly codeBlock?: string\n /** ヘッダ行の `<code>` */\n readonly codeStart?: string\n /** ヘッダ行に出るファイル名 */\n readonly codeFilename?: string\n /** 本体行の `<code>` */\n readonly codeBody?: string\n /** ハイライタを通した本体に追加で付く class */\n readonly codeHighlight?: string\n readonly table?: string\n /** `showPads` のときだけ出る、インデントぶんの余白と中点をまとめる要素 */\n readonly indentMark?: string\n /** インデント 1 段ぶんの余白 */\n readonly pad?: string\n /** インデントの右端に置く中点 */\n readonly dot?: string\n readonly internalLink?: string\n readonly externalLink?: string\n readonly projectLink?: string\n readonly hashtag?: string\n readonly inlineCode?: string\n readonly image?: string\n readonly icon?: string\n readonly formula?: string\n readonly decoration?: string\n}\n\n/** 既定の class 名。接頭辞を持たないため、利用側の CSS と名前がぶつかりうる。 */\nexport const defaultClassNames: HtmlClassNames = {\n page: 'page',\n title: 'title',\n line: 'line',\n quote: 'quote',\n monospace: 'monospace',\n codeBlock: 'code-block',\n codeStart: 'code-start',\n codeFilename: 'code-block-start',\n codeBody: 'code-body',\n codeHighlight: 'highlight',\n table: 'table',\n indentMark: 'indent-mark',\n pad: 'pad',\n dot: 'dot',\n internalLink: 'link',\n externalLink: 'link link-external',\n projectLink: 'link link-project',\n hashtag: 'hashtag',\n inlineCode: 'code',\n image: 'image',\n icon: 'icon',\n formula: 'formula',\n decoration: 'decoration',\n}\n\n/**\n * コードブロックの中身を色付けする。**戻り値は HTML としてそのまま埋め込まれる**ため、\n * エスケープは実装側の責任になる。\n *\n * `language` はファイル名から推測した名前で、拡張子があればそれ、無ければファイル名全体\n * (`code:hello.js` なら `js`、`code:python` なら `python`)。\n *\n * これを渡すと、コードブロックの本体は 1 行 1 要素ではなく 1 つの要素にまとまる。\n * ハイライタの出力が複数行にまたがるタグを含みうるため。\n */\nexport type Highlighter = (code: string, language: string) => string\n\n/**\n * 既定のハンドラの振る舞いを変えるオプション。\n *\n * `pageUrl` / `iconImageUrl` / `highlight` は、**記法に書かれていないので AST から導けない**\n * 情報を外から渡すためのもの。残りは出力の見た目を調整する。\n */\nexport interface HtmlRenderOptions {\n /**\n * ページを指す記法 (`[title]` / `[/proj/page]` / `#tag` / `[user.icon]`) の遷移先。\n * アイコンについてはリンク先だけを決め、画像は `iconImageUrl` が決める。\n *\n * `title` は記法に書かれたタイトルそのままで、`[title]` なら `title`、\n * `[/proj/page]` なら `/proj/page`、`#tag` なら `tag`、`[user.icon]` なら `user`。\n *\n * @defaultValue `/{title}`。区切りを含むタイトルは区切りを残して各段を encode する\n */\n readonly pageUrl?: (title: string, node: PageRefNode) => string\n /**\n * アイコンの画像 URL。null なら `<img>` を出さず、ユーザー名のテキストになる。\n * リンク先のほうは `pageUrl` が決める。\n *\n * @defaultValue 常に null。本家の画像 URL (`/api/pages/{project}/{user}/icon`) は\n * プロジェクト名を要するが、記法には書かれていないため\n */\n readonly iconImageUrl?: (node: IconNode) => string | null\n /** コードブロックの中身の色付け。 */\n readonly highlight?: Highlighter\n /** 出力する要素に付ける class 名。 */\n readonly classNames?: HtmlClassNames\n /**\n * インデントを本家と同じ要素として書き出す。深さ 1 段につき `pad` が 1 つ並び、\n * その右端に中点が付く。\n *\n * @defaultValue false。深さは行の `data-indent` 属性だけで表す\n */\n readonly showPads?: boolean\n}\n\nexport interface HtmlOptions extends HtmlRenderOptions {\n /** ノード型ごとの出力の上書き。指定した型だけが差し替わる。 */\n readonly handlers?: NodeHandlers<string>\n /**\n * 出力の先頭に `<style>` 要素として差し込む CSS。\n *\n * @defaultValue undefined。`<style>` を出さない\n */\n readonly style?: string\n}\n\n/**\n * ファイル名から言語名を推測する。拡張子があればそれ、無ければファイル名全体。\n * Cosense では `code:python` のように言語名だけを書くこともできるため。\n */\nconst languageOf = (filename: string): string => {\n const dot = filename.lastIndexOf('.')\n return (dot > 0 ? filename.slice(dot + 1) : filename).toLowerCase()\n}\n\n/** ハイライタに渡すコード本体。 */\nconst rawCodeOf = (node: CodeBlock): string =>\n node.lines.map((codeLine) => codeLine.value).join('\\n')\n\n/** 記法に書かれたページタイトル。ノード型ごとに置き場所が違うのをここで吸収する。 */\nconst pageTitleOf = (node: PageRefNode): string =>\n Match.value(node).pipe(\n Match.when({ type: 'hashtag' }, (tag) => tag.value),\n Match.when({ type: 'icon' }, (icon) => icon.user),\n Match.orElse((link) => link.target),\n )\n\n/**\n * `pageUrl` の既定の実装。`/proj/page` のように区切りを含むタイトルは、\n * 区切りを残したまま各段を encode する。\n */\nexport const defaultPageUrl = (title: string): string =>\n title.startsWith('/')\n ? title.split('/').map(encodeURIComponent).join('/')\n : `/${encodeURIComponent(title)}`\n\n/**\n * 装飾を表す要素を、内側から外側の順に並べたもの。\n *\n * Cosense では 1 つの記法が複数の装飾を同時に持つ (`[-/ x]` は打消しかつ斜体) ので、\n * フラグの集合を入れ子の要素に開いて表現する。\n */\nconst DECORATION_TAGS: readonly (readonly [(node: Decoration) => boolean, string])[] = [\n [(node) => node.strike, 's'],\n [(node) => node.underline, 'u'],\n [(node) => node.italic, 'em'],\n [(node) => node.bold, 'strong'],\n]\n\nconst wrapDecoration = (node: Decoration, inner: string): string =>\n DECORATION_TAGS.filter(([isOn]) => isOn(node)).reduce(\n (html, [, tag]) => `<${tag}>${html}</${tag}>`,\n inner,\n )\n\n// ---------------------------------------------------------------------------\n// 既定のハンドラ\n// ---------------------------------------------------------------------------\n\n/**\n * 既定のハンドラ一式。`toHtml` はこれに `handlers` を重ねてから走らせるので、\n * 一部だけ差し替えるのに呼ぶ必要はない。既定の出力を包みたいときに使う。\n */\nexport const createHtmlHandlers = (options: HtmlRenderOptions = {}): NodeHandlers<string> => {\n const cls: HtmlClassNames = { ...defaultClassNames, ...options.classNames }\n const highlight = options.highlight\n const pageUrl = options.pageUrl ?? defaultPageUrl\n const iconImageUrl = options.iconImageUrl ?? (() => null)\n\n const pageHref = (node: PageRefNode): Option.Option<string> =>\n nonEmpty(safeHref(pageUrl(pageTitleOf(node), node)))\n\n /** 余白の数がインデントの深さを表し、中点はその右端に付く (本家と同じ形)。 */\n const indentMark = (indent: number): string => {\n const pad = `<span${attr('class', nonEmpty(cls.pad))}> </span>`\n const dot = `<span${attr('class', nonEmpty(cls.dot))}></span>`\n return `<span${attr('class', nonEmpty(cls.indentMark))}>${pad.repeat(indent)}${dot}</span>`\n }\n\n const anchor = (\n url: Option.Option<string>,\n className: string | undefined,\n label: string,\n ): string => {\n const open = `<a${attr('class', nonEmpty(className))}${attr('href', url)}>`\n return `${open}${escapeHtml(label)}</a>`\n }\n\n return {\n page: (node, ctx) =>\n `<div${attr('class', nonEmpty(cls.page))}>${ctx.children(node).join('')}</div>`,\n\n title: (node, ctx) =>\n `<h1${attr('class', nonEmpty(cls.title))}>${ctx.children(node).join('')}</h1>`,\n\n line: (node, ctx) => {\n const body = ctx.children(node).join('')\n const styled = node.monospace\n ? `<code${attr('class', nonEmpty(cls.monospace))}>${body}</code>`\n : body\n const quoted = node.quote\n ? `<blockquote${attr('class', nonEmpty(cls.quote))}>${styled}</blockquote>`\n : styled\n // 空行も 1 行分の高さを保つ。Cosense では空行が段落の区切りとして意味を持つ。\n const inner = quoted === '' ? '<br>' : quoted\n const mark = options.showPads === true && node.indent > 0 ? indentMark(node.indent) : ''\n const open = `<div${attr('class', nonEmpty(cls.line))}${attr('data-indent', positive(node.indent))}>`\n return `${open}${mark}${inner}</div>`\n },\n\n /**\n * 1 行 = 1 要素に切る (本家と同じ)。行ごとにインデントを付けられるようにするため。\n * `highlight` があるときだけ、複数行にまたがるタグを壊さないよう本体をまとめる。\n */\n codeBlock: (node, ctx) => {\n const classes = [cls.line, cls.codeBlock].filter((name) => name !== undefined).join(' ')\n const blockLine = (indent: number, inner: string): string =>\n `<div${attr('class', nonEmpty(classes))}${attr('data-indent', positive(indent))}>${inner}</div>`\n\n // 本体はヘッダより 1 段深い。それより深い字下げは値のほうに残っている。\n const codeBody = (className: string | undefined, inner: string): string =>\n blockLine(node.indent + 1, `<code${attr('class', nonEmpty(className))}>${inner}</code>`)\n\n const filename = `<span${attr('class', nonEmpty(cls.codeFilename))}>${escapeHtml(node.filename)}</span>`\n const header = blockLine(\n node.indent,\n `<code${attr('class', nonEmpty(cls.codeStart))}>${filename}</code>`,\n )\n\n if (highlight !== undefined) {\n const className = [cls.codeBody, cls.codeHighlight]\n .filter((name) => name !== undefined)\n .join(' ')\n return header + codeBody(className, highlight(rawCodeOf(node), languageOf(node.filename)))\n }\n\n return (\n header +\n ctx\n .children(node)\n .map((value) => codeBody(cls.codeBody, value))\n .join('')\n )\n },\n codeLine: (node) => escapeHtml(node.value),\n\n table: (node, ctx) => {\n const caption = pipe(\n nonEmpty(node.name),\n Option.match({\n onNone: () => '',\n onSome: (name) => `<caption>${escapeHtml(name)}</caption>`,\n }),\n )\n const rows = ctx.children(node).join('')\n return `<table${attr('class', nonEmpty(cls.table))}>${caption}<tbody>${rows}</tbody></table>`\n },\n tableRow: (node, ctx) => `<tr>${ctx.children(node).join('')}</tr>`,\n // Cosense のテーブルにヘッダ行の概念は無いので、1 行目も含めてすべて td。\n tableCell: (node) => `<td>${escapeHtml(node.value)}</td>`,\n\n text: (node) => escapeHtml(node.value),\n\n internalLink: (node) => anchor(pageHref(node), cls.internalLink, node.label),\n externalLink: (node) => anchor(nonEmpty(safeHref(node.target)), cls.externalLink, node.label),\n projectLink: (node) => anchor(pageHref(node), cls.projectLink, node.label),\n hashtag: (node) => anchor(pageHref(node), cls.hashtag, `#${node.value}`),\n\n inlineCode: (node) =>\n `<code${attr('class', nonEmpty(cls.inlineCode))}>${escapeHtml(node.value)}</code>`,\n\n image: (node) => {\n // Gyazo のページ URL のように、書かれたままでは <img> に入らない URL をここで直す。\n // AST はソースの文字列を保つ約束なので、表示用への変換は描画側の責任になる。\n const src = asImageSrc(node.src) ?? node.src\n const open = `<img${attr('class', nonEmpty(cls.image))}${attr('src', nonEmpty(safeSrc(src)))}`\n return linked(`${open} alt=\"\"${attr('data-large', flag(node.large))}>`, node.link)\n },\n\n // 本家と同じく、そのユーザーのページへのリンクで画像を包む。\n icon: (node) => {\n const name = escapeHtml(node.user)\n const src = iconImageUrl(node)\n const body =\n src === null\n ? name\n : `<img${attr('class', nonEmpty(cls.icon))}${attr('src', nonEmpty(safeSrc(src)))} alt=\"${name}\" title=\"${name}\">`\n const className = [cls.internalLink, cls.icon].filter((c) => c !== undefined).join(' ')\n const open = `<a${attr('class', nonEmpty(className))}${attr('href', pageHref(node))}>`\n return `${open}${body}</a>`.repeat(node.count)\n },\n\n // 数式の組版は KaTeX 等の仕事なので、記法を外した中身をそのまま置く。\n formula: (node) =>\n `<span${attr('class', nonEmpty(cls.formula))}>${escapeHtml(node.value)}</span>`,\n\n decoration: (node, ctx) => {\n const open = `<span${attr('class', nonEmpty(cls.decoration))}${attr('data-size-level', positive(node.sizeLevel))}>`\n return `${open}${wrapDecoration(node, ctx.children(node).join(''))}</span>`\n },\n }\n}\n\n/**\n * ページ (または任意のノード) を HTML 文字列にする。\n *\n * テキストと属性値はエスケープし、`javascript:` のようなスキームの URL は属性ごと落とす。\n * `handlers` と `highlight` が返した文字列はそのまま埋め込むので、\n * そこでのエスケープは書いた人の責任になる。\n */\nexport const toHtml = (node: AnyNode, options: HtmlOptions = {}): string => {\n const handlers: NodeHandlers<string> = { ...createHtmlHandlers(options), ...options.handlers }\n const compile = createCompiler<string>({\n handlers,\n // ハンドラの無いノード (拡張が足した独自ノード) でも中身は落とさない。\n fallback: (other, ctx) => ctx.children(other).join(''),\n })\n const html = compile(node)\n return options.style === undefined ? html : `<style>${options.style}</style>${html}`\n}\n","/**\n * to-plain-text.ts — AST を記法抜きのテキストに戻す。\n *\n * `createCompiler` のハンドラ機構をひととおり使った参照実装も兼ねる。\n */\nimport type { AnyNode } from '../types'\nimport { type NodeHandlers, createCompiler } from './create-compiler'\n\nconst INDENT_UNIT = ' '\n\nconst handlers: NodeHandlers<string> = {\n page: (node, ctx) => ctx.children(node).join('\\n'),\n\n title: (node) => node.value,\n\n line: (node, ctx) => {\n const body = ctx.children(node).join('')\n const quote = node.quote ? '> ' : ''\n return INDENT_UNIT.repeat(node.indent) + quote + body\n },\n\n codeBlock: (node, ctx) =>\n [`${INDENT_UNIT.repeat(node.indent)}${node.filename}`, ...ctx.children(node)].join('\\n'),\n codeLine: (node) => node.value,\n\n table: (node, ctx) => [node.name, ...ctx.children(node)].join('\\n'),\n tableRow: (node, ctx) => ctx.children(node).join('\\t'),\n tableCell: (node) => node.value,\n\n text: (node) => node.value,\n internalLink: (node) => node.label,\n externalLink: (node) => node.label,\n projectLink: (node) => node.label,\n hashtag: (node) => node.value,\n inlineCode: (node) => node.value,\n formula: (node) => node.value,\n icon: (node) => node.user,\n image: (node) => node.src,\n decoration: (node, ctx) => ctx.children(node).join(''),\n}\n\n// ハンドラの無いノード (拡張が足した独自ノード) でも中身は落とさない。\nconst compile = createCompiler<string>({\n handlers,\n fallback: (node, ctx) => ctx.children(node).join(''),\n})\n\n/** ページ (または任意のノード) を記法抜きのテキストにする。 */\nexport const toPlainText = (node: AnyNode): string => compile(node)\n"],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { D as ProjectLink, b as InternalLink, f as Hashtag, p as IconNode, t as AnyNode } from "./types-Bo_BrmvK.cjs";
|
|
2
|
+
import { a as createCompiler, i as NodeHandlers, n as CompilerOptions, r as NodeHandler, t as CompileContext } from "./create-compiler-CvlndKOA.cjs";
|
|
3
|
+
//#region src/compile/to-html.d.ts
|
|
4
|
+
/** `& < > " '` を実体参照に置き換える。テキストと属性値のどちらにも使える。 */
|
|
5
|
+
declare const escapeHtml: (value: string) => string;
|
|
6
|
+
/** `href` に入れて安全な URL だけを返す。script が動くスキームなら null。 */
|
|
7
|
+
declare const safeHref: (url: string) => string | null;
|
|
8
|
+
/** `src` に入れて安全な URL だけを返す。script が動くスキームなら null。 */
|
|
9
|
+
declare const safeSrc: (url: string) => string | null;
|
|
10
|
+
/** プロジェクト内のページを指すノード。本家ではアイコンもユーザーのページへのリンクになる。 */
|
|
11
|
+
type PageRefNode = InternalLink | ProjectLink | Hashtag | IconNode;
|
|
12
|
+
/**
|
|
13
|
+
* 出力する要素に付ける class 名。指定したキーだけが既定を上書きする。
|
|
14
|
+
* 空文字にすると class 属性自体を出さない。
|
|
15
|
+
*/
|
|
16
|
+
interface HtmlClassNames {
|
|
17
|
+
readonly page?: string;
|
|
18
|
+
readonly title?: string;
|
|
19
|
+
readonly line?: string;
|
|
20
|
+
/** 引用行の `<blockquote>` */
|
|
21
|
+
readonly quote?: string;
|
|
22
|
+
/** 等幅行の `<code>` */
|
|
23
|
+
readonly monospace?: string;
|
|
24
|
+
/** コードブロックに属する行 (ヘッダ行と本体行の両方) */
|
|
25
|
+
readonly codeBlock?: string;
|
|
26
|
+
/** ヘッダ行の `<code>` */
|
|
27
|
+
readonly codeStart?: string;
|
|
28
|
+
/** ヘッダ行に出るファイル名 */
|
|
29
|
+
readonly codeFilename?: string;
|
|
30
|
+
/** 本体行の `<code>` */
|
|
31
|
+
readonly codeBody?: string;
|
|
32
|
+
/** ハイライタを通した本体に追加で付く class */
|
|
33
|
+
readonly codeHighlight?: string;
|
|
34
|
+
readonly table?: string;
|
|
35
|
+
/** `showPads` のときだけ出る、インデントぶんの余白と中点をまとめる要素 */
|
|
36
|
+
readonly indentMark?: string;
|
|
37
|
+
/** インデント 1 段ぶんの余白 */
|
|
38
|
+
readonly pad?: string;
|
|
39
|
+
/** インデントの右端に置く中点 */
|
|
40
|
+
readonly dot?: string;
|
|
41
|
+
readonly internalLink?: string;
|
|
42
|
+
readonly externalLink?: string;
|
|
43
|
+
readonly projectLink?: string;
|
|
44
|
+
readonly hashtag?: string;
|
|
45
|
+
readonly inlineCode?: string;
|
|
46
|
+
readonly image?: string;
|
|
47
|
+
readonly icon?: string;
|
|
48
|
+
readonly formula?: string;
|
|
49
|
+
readonly decoration?: string;
|
|
50
|
+
}
|
|
51
|
+
/** 既定の class 名。接頭辞を持たないため、利用側の CSS と名前がぶつかりうる。 */
|
|
52
|
+
declare const defaultClassNames: HtmlClassNames;
|
|
53
|
+
/**
|
|
54
|
+
* コードブロックの中身を色付けする。**戻り値は HTML としてそのまま埋め込まれる**ため、
|
|
55
|
+
* エスケープは実装側の責任になる。
|
|
56
|
+
*
|
|
57
|
+
* `language` はファイル名から推測した名前で、拡張子があればそれ、無ければファイル名全体
|
|
58
|
+
* (`code:hello.js` なら `js`、`code:python` なら `python`)。
|
|
59
|
+
*
|
|
60
|
+
* これを渡すと、コードブロックの本体は 1 行 1 要素ではなく 1 つの要素にまとまる。
|
|
61
|
+
* ハイライタの出力が複数行にまたがるタグを含みうるため。
|
|
62
|
+
*/
|
|
63
|
+
type Highlighter = (code: string, language: string) => string;
|
|
64
|
+
/**
|
|
65
|
+
* 既定のハンドラの振る舞いを変えるオプション。
|
|
66
|
+
*
|
|
67
|
+
* `pageUrl` / `iconImageUrl` / `highlight` は、**記法に書かれていないので AST から導けない**
|
|
68
|
+
* 情報を外から渡すためのもの。残りは出力の見た目を調整する。
|
|
69
|
+
*/
|
|
70
|
+
interface HtmlRenderOptions {
|
|
71
|
+
/**
|
|
72
|
+
* ページを指す記法 (`[title]` / `[/proj/page]` / `#tag` / `[user.icon]`) の遷移先。
|
|
73
|
+
* アイコンについてはリンク先だけを決め、画像は `iconImageUrl` が決める。
|
|
74
|
+
*
|
|
75
|
+
* `title` は記法に書かれたタイトルそのままで、`[title]` なら `title`、
|
|
76
|
+
* `[/proj/page]` なら `/proj/page`、`#tag` なら `tag`、`[user.icon]` なら `user`。
|
|
77
|
+
*
|
|
78
|
+
* @defaultValue `/{title}`。区切りを含むタイトルは区切りを残して各段を encode する
|
|
79
|
+
*/
|
|
80
|
+
readonly pageUrl?: (title: string, node: PageRefNode) => string;
|
|
81
|
+
/**
|
|
82
|
+
* アイコンの画像 URL。null なら `<img>` を出さず、ユーザー名のテキストになる。
|
|
83
|
+
* リンク先のほうは `pageUrl` が決める。
|
|
84
|
+
*
|
|
85
|
+
* @defaultValue 常に null。本家の画像 URL (`/api/pages/{project}/{user}/icon`) は
|
|
86
|
+
* プロジェクト名を要するが、記法には書かれていないため
|
|
87
|
+
*/
|
|
88
|
+
readonly iconImageUrl?: (node: IconNode) => string | null;
|
|
89
|
+
/** コードブロックの中身の色付け。 */
|
|
90
|
+
readonly highlight?: Highlighter;
|
|
91
|
+
/** 出力する要素に付ける class 名。 */
|
|
92
|
+
readonly classNames?: HtmlClassNames;
|
|
93
|
+
/**
|
|
94
|
+
* インデントを本家と同じ要素として書き出す。深さ 1 段につき `pad` が 1 つ並び、
|
|
95
|
+
* その右端に中点が付く。
|
|
96
|
+
*
|
|
97
|
+
* @defaultValue false。深さは行の `data-indent` 属性だけで表す
|
|
98
|
+
*/
|
|
99
|
+
readonly showPads?: boolean;
|
|
100
|
+
}
|
|
101
|
+
interface HtmlOptions extends HtmlRenderOptions {
|
|
102
|
+
/** ノード型ごとの出力の上書き。指定した型だけが差し替わる。 */
|
|
103
|
+
readonly handlers?: NodeHandlers<string>;
|
|
104
|
+
/**
|
|
105
|
+
* 出力の先頭に `<style>` 要素として差し込む CSS。
|
|
106
|
+
*
|
|
107
|
+
* @defaultValue undefined。`<style>` を出さない
|
|
108
|
+
*/
|
|
109
|
+
readonly style?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `pageUrl` の既定の実装。`/proj/page` のように区切りを含むタイトルは、
|
|
113
|
+
* 区切りを残したまま各段を encode する。
|
|
114
|
+
*/
|
|
115
|
+
declare const defaultPageUrl: (title: string) => string;
|
|
116
|
+
/**
|
|
117
|
+
* 既定のハンドラ一式。`toHtml` はこれに `handlers` を重ねてから走らせるので、
|
|
118
|
+
* 一部だけ差し替えるのに呼ぶ必要はない。既定の出力を包みたいときに使う。
|
|
119
|
+
*/
|
|
120
|
+
declare const createHtmlHandlers: (options?: HtmlRenderOptions) => NodeHandlers<string>;
|
|
121
|
+
/**
|
|
122
|
+
* ページ (または任意のノード) を HTML 文字列にする。
|
|
123
|
+
*
|
|
124
|
+
* テキストと属性値はエスケープし、`javascript:` のようなスキームの URL は属性ごと落とす。
|
|
125
|
+
* `handlers` と `highlight` が返した文字列はそのまま埋め込むので、
|
|
126
|
+
* そこでのエスケープは書いた人の責任になる。
|
|
127
|
+
*/
|
|
128
|
+
declare const toHtml: (node: AnyNode, options?: HtmlOptions) => string;
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/compile/to-plain-text.d.ts
|
|
131
|
+
/** ページ (または任意のノード) を記法抜きのテキストにする。 */
|
|
132
|
+
declare const toPlainText: (node: AnyNode) => string;
|
|
133
|
+
//#endregion
|
|
134
|
+
export { type CompileContext, type CompilerOptions, type Highlighter, type HtmlClassNames, type HtmlOptions, type HtmlRenderOptions, type NodeHandler, type NodeHandlers, type PageRefNode, createCompiler, createHtmlHandlers, defaultClassNames, defaultPageUrl, escapeHtml, safeHref, safeSrc, toHtml, toPlainText };
|
|
135
|
+
//# sourceMappingURL=compile.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.cts","names":[],"sources":["../src/compile/to-html.ts","../src/compile/to-plain-text.ts"],"mappings":";;;;cAgCa,aAAc;;cAqCd,WAAY;;cAGZ,UAAW;;KAkBZ,cAAc,eAAe,cAAc,UAAU;;;;;UAMhD;WACN;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;cAIE,mBAAmB;;;;;;;;;;;KAoCpB,eAAe,cAAc;;;;;;;UAQxB;;;;;;;;;;WAUN,WAAW,eAAe,MAAM;;;;;;;;WAQhC,gBAAgB,MAAM;;WAEtB,YAAY;;WAEZ,aAAa;;;;;;;WAOb;;UAGM,oBAAoB;;WAE1B,WAAW;;;;;;WAMX;;;;;;cA4BE,iBAAkB;;;;;cAgClB,qBAAsB,UAAS,sBAAyB;;;;;;;;cAmJxD,SAAU,MAAM,SAAS,UAAS;;;;cCxXlC,cAAe,MAAM"}
|