md2rfc 0.0.1
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/README.md +26 -0
- package/biome.json +24 -0
- package/bun.lock +58 -0
- package/dist/Cli.d.ts +3 -0
- package/dist/Cli.d.ts.map +1 -0
- package/dist/Cli.js +54 -0
- package/dist/Cli.js.map +1 -0
- package/dist/Document.d.ts +48 -0
- package/dist/Document.d.ts.map +1 -0
- package/dist/Document.js +426 -0
- package/dist/Document.js.map +1 -0
- package/dist/Xml.d.ts +4 -0
- package/dist/Xml.d.ts.map +1 -0
- package/dist/Xml.js +357 -0
- package/dist/Xml.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
- package/src/Cli.ts +61 -0
- package/src/Document.ts +501 -0
- package/src/Xml.ts +420 -0
- package/src/index.ts +2 -0
- package/tsconfig.json +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# md2rfc
|
|
2
|
+
|
|
3
|
+
Convert Markdown to xml2rfc v3 format (RFC 7991).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
md2rfc is a tool for converting Markdown documents to xml2rfc v3 XML format, suitable for IETF Internet-Draft submissions.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx md2rfc <input.md>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Example
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx md2rfc draft-spec.md
|
|
21
|
+
# Converted draft-spec.md → draft-spec.xml
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
MIT
|
package/biome.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
|
|
3
|
+
"linter": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"rules": {
|
|
6
|
+
"recommended": true
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"formatter": {
|
|
10
|
+
"enabled": true,
|
|
11
|
+
"indentStyle": "space",
|
|
12
|
+
"indentWidth": 2,
|
|
13
|
+
"lineWidth": 100
|
|
14
|
+
},
|
|
15
|
+
"assist": {
|
|
16
|
+
"actions": {
|
|
17
|
+
"source": {
|
|
18
|
+
"organizeImports": {
|
|
19
|
+
"level": "on"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/bun.lock
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "md2xml",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@biomejs/biome": "^2.3.11",
|
|
9
|
+
"@types/bun": "latest",
|
|
10
|
+
"typescript": "^5.9.3",
|
|
11
|
+
"zile": "^0.0.15",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
"packages": {
|
|
16
|
+
"@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="],
|
|
17
|
+
|
|
18
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="],
|
|
19
|
+
|
|
20
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="],
|
|
21
|
+
|
|
22
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="],
|
|
23
|
+
|
|
24
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="],
|
|
25
|
+
|
|
26
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="],
|
|
27
|
+
|
|
28
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="],
|
|
29
|
+
|
|
30
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="],
|
|
31
|
+
|
|
32
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="],
|
|
33
|
+
|
|
34
|
+
"@clack/core": ["@clack/core@1.0.0-alpha.7", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-3vdh6Ar09D14rVxJZIm3VQJkU+ZOKKT5I5cC0cOVazy70CNyYYjiwRj9unwalhESndgxx6bGc/m6Hhs4EKF5XQ=="],
|
|
35
|
+
|
|
36
|
+
"@clack/prompts": ["@clack/prompts@1.0.0-alpha.9", "", { "dependencies": { "@clack/core": "1.0.0-alpha.7", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-sKs0UjiHFWvry4SiRfBi5Qnj0C/6AYx8aKkFPZQSuUZXgAram25ZDmhQmP7vj1aFyLpfHWtLQjWvOvcat0TOLg=="],
|
|
37
|
+
|
|
38
|
+
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
|
|
39
|
+
|
|
40
|
+
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
|
41
|
+
|
|
42
|
+
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
|
|
43
|
+
|
|
44
|
+
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
|
45
|
+
|
|
46
|
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
|
47
|
+
|
|
48
|
+
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
|
49
|
+
|
|
50
|
+
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
|
51
|
+
|
|
52
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
53
|
+
|
|
54
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
55
|
+
|
|
56
|
+
"zile": ["zile@0.0.15", "", { "dependencies": { "@clack/prompts": "^1.0.0-alpha.6", "cac": "6.7.14", "tsconfck": "3.1.6" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0", "typescript": ">=5" }, "optionalPeers": ["@typescript/native-preview", "typescript"], "bin": { "zile": "dist/Cli.js" } }, "sha512-2YECNiU5D7UM7AAvp2u+/Y8mdn0V4WiTZi8WrwMjDWFsDQ6WDvW0OxHB9JOgRg1N9hcYIyjfdTBGeU8vuf0v7A=="],
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/Cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cli.d.ts","sourceRoot":"","sources":["../src/Cli.ts"],"names":[],"mappings":""}
|
package/dist/Cli.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import * as Fs from "node:fs";
|
|
3
|
+
import * as Path from "node:path";
|
|
4
|
+
import * as Xml from "./Xml.js";
|
|
5
|
+
function main() {
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
8
|
+
console.log(`
|
|
9
|
+
md2rfc - Convert Markdown to xml2rfc v3 format
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
md2rfc <input.md> [-o <output.xml>]
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
-o, --output Output file path (default: input with .xml extension)
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
md2rfc draft.md
|
|
19
|
+
md2rfc draft.md -o out/draft.xml
|
|
20
|
+
`);
|
|
21
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
22
|
+
}
|
|
23
|
+
const outputIndex = args.findIndex((a) => a === "-o" || a === "--output");
|
|
24
|
+
let outputPath;
|
|
25
|
+
let inputPath;
|
|
26
|
+
if (outputIndex !== -1) {
|
|
27
|
+
outputPath = args[outputIndex + 1];
|
|
28
|
+
inputPath = args.filter((_, i) => i !== outputIndex && i !== outputIndex + 1)[0];
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
inputPath = args[0];
|
|
32
|
+
}
|
|
33
|
+
if (!inputPath) {
|
|
34
|
+
console.error("Error: No input file specified");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
outputPath ??= inputPath.replace(/\.md$/, ".xml");
|
|
38
|
+
try {
|
|
39
|
+
const markdown = Fs.readFileSync(inputPath, "utf-8");
|
|
40
|
+
const xml = Xml.fromMarkdown(markdown);
|
|
41
|
+
const outputDir = Path.dirname(outputPath);
|
|
42
|
+
if (outputDir && !Fs.existsSync(outputDir)) {
|
|
43
|
+
Fs.mkdirSync(outputDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
Fs.writeFileSync(outputPath, xml);
|
|
46
|
+
console.log(`Converted ${inputPath} → ${outputPath}`);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
main();
|
|
54
|
+
//# sourceMappingURL=Cli.js.map
|
package/dist/Cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cli.js","sourceRoot":"","sources":["../src/Cli.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,SAAS,IAAI;IACX,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;CAYf,CAAC,CAAC;QACC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,UAAU,CAAC,CAAC;IAC1E,IAAI,UAA8B,CAAC;IACnC,IAAI,SAA6B,CAAC;IAElC,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACvB,UAAU,GAAG,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QACnC,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;SAAM,CAAC;QACN,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,KAAK,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEvC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3C,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,MAAM,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface Author {
|
|
2
|
+
fullname: string;
|
|
3
|
+
email?: string;
|
|
4
|
+
organization?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface Meta {
|
|
7
|
+
title: string;
|
|
8
|
+
docName: string;
|
|
9
|
+
category?: string;
|
|
10
|
+
ipr?: string;
|
|
11
|
+
submissionType?: string;
|
|
12
|
+
consensus?: boolean;
|
|
13
|
+
authors: Author[];
|
|
14
|
+
abstract?: string;
|
|
15
|
+
area?: string;
|
|
16
|
+
workgroup?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Section {
|
|
19
|
+
anchor: string;
|
|
20
|
+
name: string;
|
|
21
|
+
content: string[];
|
|
22
|
+
children: Section[];
|
|
23
|
+
}
|
|
24
|
+
export interface CustomReference {
|
|
25
|
+
anchor: string;
|
|
26
|
+
originalName: string;
|
|
27
|
+
title: string;
|
|
28
|
+
author?: string;
|
|
29
|
+
date?: string;
|
|
30
|
+
target?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Document {
|
|
33
|
+
meta: Meta;
|
|
34
|
+
statusOfMemo?: string;
|
|
35
|
+
copyrightNotice?: string;
|
|
36
|
+
sections: Section[];
|
|
37
|
+
references: {
|
|
38
|
+
normative: string[];
|
|
39
|
+
informative: string[];
|
|
40
|
+
customNormative: CustomReference[];
|
|
41
|
+
customInformative: CustomReference[];
|
|
42
|
+
};
|
|
43
|
+
appendices: Section[];
|
|
44
|
+
acknowledgements?: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function fromMarkdown(content: string): Document;
|
|
47
|
+
export declare function wrapBcp14Keywords(text: string): string;
|
|
48
|
+
//# sourceMappingURL=Document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Document.d.ts","sourceRoot":"","sources":["../src/Document.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,eAAe,EAAE,eAAe,EAAE,CAAC;QACnC,iBAAiB,EAAE,eAAe,EAAE,CAAC;KACtC,CAAC;IACF,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAgBD,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAsBtD;AAuZD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOtD"}
|
package/dist/Document.js
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
const BCP14_KEYWORDS = [
|
|
2
|
+
"MUST NOT",
|
|
3
|
+
"SHALL NOT",
|
|
4
|
+
"SHOULD NOT",
|
|
5
|
+
"NOT RECOMMENDED",
|
|
6
|
+
"MUST",
|
|
7
|
+
"REQUIRED",
|
|
8
|
+
"SHALL",
|
|
9
|
+
"SHOULD",
|
|
10
|
+
"RECOMMENDED",
|
|
11
|
+
"MAY",
|
|
12
|
+
"OPTIONAL",
|
|
13
|
+
];
|
|
14
|
+
export function fromMarkdown(content) {
|
|
15
|
+
const { frontmatter, body } = extractFrontmatter(content);
|
|
16
|
+
const lines = body.split("\n");
|
|
17
|
+
const meta = extractMeta(frontmatter);
|
|
18
|
+
const abstract = extractAbstract(lines);
|
|
19
|
+
const statusOfMemo = extractSection(lines, "Status of This Memo");
|
|
20
|
+
const copyrightNotice = extractSection(lines, "Copyright Notice");
|
|
21
|
+
const sections = extractSections(lines);
|
|
22
|
+
const references = extractReferences(lines);
|
|
23
|
+
const appendices = extractAppendices(lines);
|
|
24
|
+
const acknowledgements = extractSection(lines, "Acknowledgements");
|
|
25
|
+
return {
|
|
26
|
+
meta: { ...meta, abstract },
|
|
27
|
+
statusOfMemo,
|
|
28
|
+
copyrightNotice,
|
|
29
|
+
sections,
|
|
30
|
+
references,
|
|
31
|
+
appendices,
|
|
32
|
+
acknowledgements,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function extractFrontmatter(content) {
|
|
36
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
37
|
+
if (!match) {
|
|
38
|
+
return { frontmatter: {}, body: content };
|
|
39
|
+
}
|
|
40
|
+
const [, yaml, body] = match;
|
|
41
|
+
const frontmatter = parseYaml(yaml);
|
|
42
|
+
return { frontmatter, body };
|
|
43
|
+
}
|
|
44
|
+
function parseYaml(yaml) {
|
|
45
|
+
const result = {};
|
|
46
|
+
const lines = yaml.split("\n");
|
|
47
|
+
let _currentKey = null;
|
|
48
|
+
let currentArray = null;
|
|
49
|
+
let inArray = false;
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
if (line.trim() === "")
|
|
52
|
+
continue;
|
|
53
|
+
const arrayItemMatch = line.match(/^ {2}- (.+)$/);
|
|
54
|
+
const nestedObjectMatch = line.match(/^ {2}- (\w+):\s*(.*)$/);
|
|
55
|
+
const keyValueMatch = line.match(/^(\w+):\s*(.*)$/);
|
|
56
|
+
if (nestedObjectMatch && inArray && currentArray) {
|
|
57
|
+
const [, key, value] = nestedObjectMatch;
|
|
58
|
+
const obj = { [key]: value };
|
|
59
|
+
currentArray.push(obj);
|
|
60
|
+
}
|
|
61
|
+
else if (inArray && currentArray && line.match(/^ {4}(\w+):\s*(.*)$/)) {
|
|
62
|
+
const nestedKeyValue = line.match(/^ {4}(\w+):\s*(.*)$/);
|
|
63
|
+
if (nestedKeyValue) {
|
|
64
|
+
const [, key, value] = nestedKeyValue;
|
|
65
|
+
const lastItem = currentArray[currentArray.length - 1];
|
|
66
|
+
if (lastItem && typeof lastItem === "object") {
|
|
67
|
+
lastItem[key] = value;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (arrayItemMatch && inArray && currentArray) {
|
|
72
|
+
currentArray.push(arrayItemMatch[1]);
|
|
73
|
+
}
|
|
74
|
+
else if (keyValueMatch) {
|
|
75
|
+
const [, key, value] = keyValueMatch;
|
|
76
|
+
if (value === "") {
|
|
77
|
+
_currentKey = key;
|
|
78
|
+
currentArray = [];
|
|
79
|
+
inArray = true;
|
|
80
|
+
result[key] = currentArray;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
inArray = false;
|
|
84
|
+
currentArray = null;
|
|
85
|
+
if (value === "true") {
|
|
86
|
+
result[key] = true;
|
|
87
|
+
}
|
|
88
|
+
else if (value === "false") {
|
|
89
|
+
result[key] = false;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
result[key] = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
function extractMeta(frontmatter) {
|
|
100
|
+
const title = frontmatter.title || "";
|
|
101
|
+
const docName = frontmatter.docName || "";
|
|
102
|
+
const category = frontmatter.category;
|
|
103
|
+
const ipr = frontmatter.ipr;
|
|
104
|
+
const submissionType = frontmatter.submissionType;
|
|
105
|
+
const consensus = frontmatter.consensus;
|
|
106
|
+
const area = frontmatter.area;
|
|
107
|
+
const workgroup = frontmatter.workgroup;
|
|
108
|
+
const authors = [];
|
|
109
|
+
const rawAuthors = frontmatter.author;
|
|
110
|
+
if (rawAuthors) {
|
|
111
|
+
for (const author of rawAuthors) {
|
|
112
|
+
authors.push({
|
|
113
|
+
fullname: author.fullname || "",
|
|
114
|
+
email: author.email,
|
|
115
|
+
organization: author.organization,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { title, docName, category, ipr, submissionType, consensus, authors, area, workgroup };
|
|
120
|
+
}
|
|
121
|
+
function extractAbstract(lines) {
|
|
122
|
+
const startIdx = lines.findIndex((l) => /^##\s+Abstract/i.test(l));
|
|
123
|
+
if (startIdx === -1)
|
|
124
|
+
return undefined;
|
|
125
|
+
const content = [];
|
|
126
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
127
|
+
if (lines[i].startsWith("## ") || lines[i] === "---")
|
|
128
|
+
break;
|
|
129
|
+
content.push(lines[i]);
|
|
130
|
+
}
|
|
131
|
+
return content.join("\n").trim();
|
|
132
|
+
}
|
|
133
|
+
function extractSection(lines, sectionName) {
|
|
134
|
+
const regex = new RegExp(`^##\\s+${sectionName}`, "i");
|
|
135
|
+
const startIdx = lines.findIndex((l) => regex.test(l));
|
|
136
|
+
if (startIdx === -1)
|
|
137
|
+
return undefined;
|
|
138
|
+
const content = [];
|
|
139
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
140
|
+
if (lines[i].startsWith("## ") || lines[i] === "---")
|
|
141
|
+
break;
|
|
142
|
+
content.push(lines[i]);
|
|
143
|
+
}
|
|
144
|
+
return content.join("\n").trim();
|
|
145
|
+
}
|
|
146
|
+
function extractSections(lines) {
|
|
147
|
+
const sections = [];
|
|
148
|
+
let currentSection = null;
|
|
149
|
+
let currentSubsection = null;
|
|
150
|
+
let currentSubsubsection = null;
|
|
151
|
+
let inCodeBlock = false;
|
|
152
|
+
const skipSections = [
|
|
153
|
+
"abstract",
|
|
154
|
+
"status of this memo",
|
|
155
|
+
"copyright notice",
|
|
156
|
+
"table of contents",
|
|
157
|
+
"acknowledgements",
|
|
158
|
+
"authors' addresses",
|
|
159
|
+
"references",
|
|
160
|
+
"appendix",
|
|
161
|
+
];
|
|
162
|
+
for (let i = 0; i < lines.length; i++) {
|
|
163
|
+
const line = lines[i];
|
|
164
|
+
if (line.startsWith("```")) {
|
|
165
|
+
inCodeBlock = !inCodeBlock;
|
|
166
|
+
}
|
|
167
|
+
if (inCodeBlock) {
|
|
168
|
+
if (currentSubsubsection) {
|
|
169
|
+
currentSubsubsection.content.push(line);
|
|
170
|
+
}
|
|
171
|
+
else if (currentSubsection) {
|
|
172
|
+
currentSubsection.content.push(line);
|
|
173
|
+
}
|
|
174
|
+
else if (currentSection) {
|
|
175
|
+
currentSection.content.push(line);
|
|
176
|
+
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const h2Match = line.match(/^##\s+(\d+)\.\s+(.+)/);
|
|
180
|
+
const h3Match = line.match(/^###\s+(\d+)\.(\d+)\.\s+(.+)/);
|
|
181
|
+
const h4Match = line.match(/^####\s+(\d+)\.(\d+)\.(\d+)\.\s+(.+)/);
|
|
182
|
+
const appendixMatch = line.match(/^##\s+Appendix\s+([A-Z])[:.]?\s*(.+)?/i);
|
|
183
|
+
const unnumberedH2 = line.match(/^##\s+(.+)/);
|
|
184
|
+
if (h4Match) {
|
|
185
|
+
const [, _sec, _sub, _subsub, name] = h4Match;
|
|
186
|
+
currentSubsubsection = {
|
|
187
|
+
anchor: slugify(name.trim()),
|
|
188
|
+
name: name.trim(),
|
|
189
|
+
content: [],
|
|
190
|
+
children: [],
|
|
191
|
+
};
|
|
192
|
+
if (currentSubsection) {
|
|
193
|
+
currentSubsection.children.push(currentSubsubsection);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (h3Match) {
|
|
197
|
+
const [, _sec, _sub, name] = h3Match;
|
|
198
|
+
currentSubsubsection = null;
|
|
199
|
+
currentSubsection = {
|
|
200
|
+
anchor: slugify(name.trim()),
|
|
201
|
+
name: name.trim(),
|
|
202
|
+
content: [],
|
|
203
|
+
children: [],
|
|
204
|
+
};
|
|
205
|
+
if (currentSection) {
|
|
206
|
+
currentSection.children.push(currentSubsection);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (h2Match) {
|
|
210
|
+
const [, _num, name] = h2Match;
|
|
211
|
+
const nameLower = name.toLowerCase();
|
|
212
|
+
currentSubsubsection = null;
|
|
213
|
+
currentSubsection = null;
|
|
214
|
+
// Skip sections that are handled separately (references, appendices, etc.)
|
|
215
|
+
if (skipSections.some((s) => nameLower.startsWith(s))) {
|
|
216
|
+
currentSection = null;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
currentSection = {
|
|
220
|
+
anchor: slugify(name.trim()),
|
|
221
|
+
name: name.trim(),
|
|
222
|
+
content: [],
|
|
223
|
+
children: [],
|
|
224
|
+
};
|
|
225
|
+
sections.push(currentSection);
|
|
226
|
+
}
|
|
227
|
+
else if (appendixMatch || unnumberedH2) {
|
|
228
|
+
if (unnumberedH2) {
|
|
229
|
+
const name = unnumberedH2[1].toLowerCase();
|
|
230
|
+
if (skipSections.some((s) => name.startsWith(s))) {
|
|
231
|
+
currentSection = null;
|
|
232
|
+
currentSubsection = null;
|
|
233
|
+
currentSubsubsection = null;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (name.includes("reference")) {
|
|
237
|
+
currentSection = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (line === "---") {
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
if (currentSubsubsection) {
|
|
245
|
+
currentSubsubsection.content.push(line);
|
|
246
|
+
}
|
|
247
|
+
else if (currentSubsection) {
|
|
248
|
+
currentSubsection.content.push(line);
|
|
249
|
+
}
|
|
250
|
+
else if (currentSection) {
|
|
251
|
+
currentSection.content.push(line);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return sections;
|
|
256
|
+
}
|
|
257
|
+
function extractReferences(lines) {
|
|
258
|
+
const normative = new Set();
|
|
259
|
+
const informative = new Set();
|
|
260
|
+
const customNormative = [];
|
|
261
|
+
const customInformative = [];
|
|
262
|
+
let currentSection = null;
|
|
263
|
+
for (let i = 0; i < lines.length; i++) {
|
|
264
|
+
const line = lines[i];
|
|
265
|
+
if (/^###?\s+(\d+\.\d+\.?\s+)?Normative References/i.test(line)) {
|
|
266
|
+
currentSection = "normative";
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (/^###?\s+(\d+\.\d+\.?\s+)?Informative References/i.test(line)) {
|
|
270
|
+
currentSection = "informative";
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (/^###?\s+(\d+\.|\d+\.\d+\.?)?\s*[A-Z]/.test(line) && !line.includes("References")) {
|
|
274
|
+
currentSection = null;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (currentSection) {
|
|
278
|
+
// Check for RFC references
|
|
279
|
+
const rfcMatches = line.matchAll(/\[RFC(\d+)\]/g);
|
|
280
|
+
for (const match of rfcMatches) {
|
|
281
|
+
if (currentSection === "normative") {
|
|
282
|
+
normative.add(match[1]);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
informative.add(match[1]);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Check for custom references: - **[Anchor]** "Title", <URL>.
|
|
289
|
+
// Or: - **[Anchor]** Author, "Title", <URL>.
|
|
290
|
+
// Or: - **[Anchor]** "Title", Work in Progress.
|
|
291
|
+
const customRefMatch = line.match(/^-\s+\*\*\[([^\]]+)\]\*\*\s+(.+)$/);
|
|
292
|
+
if (customRefMatch && !/^\[RFC\d+\]$/.test(`[${customRefMatch[1]}]`)) {
|
|
293
|
+
const anchor = customRefMatch[1];
|
|
294
|
+
const rest = customRefMatch[2];
|
|
295
|
+
// Collect continuation lines (indented lines that follow)
|
|
296
|
+
let fullText = rest;
|
|
297
|
+
while (i + 1 < lines.length && /^\s{2,}/.test(lines[i + 1]) && !lines[i + 1].match(/^-\s+\*\*/)) {
|
|
298
|
+
i++;
|
|
299
|
+
fullText += " " + lines[i].trim();
|
|
300
|
+
}
|
|
301
|
+
// Parse the reference text
|
|
302
|
+
// Format 1: "Title", <URL>.
|
|
303
|
+
// Format 2: Author, "Title", <URL>.
|
|
304
|
+
// Format 3: Author, "Title", Work in Progress.
|
|
305
|
+
let title = "";
|
|
306
|
+
let author;
|
|
307
|
+
let target;
|
|
308
|
+
// Extract URL if present
|
|
309
|
+
const urlMatch = fullText.match(/<([^>]+)>/);
|
|
310
|
+
if (urlMatch) {
|
|
311
|
+
target = urlMatch[1];
|
|
312
|
+
fullText = fullText.replace(/<[^>]+>\.?/, "").trim();
|
|
313
|
+
}
|
|
314
|
+
// Extract title in quotes
|
|
315
|
+
const titleMatch = fullText.match(/"([^"]+)"/);
|
|
316
|
+
if (titleMatch) {
|
|
317
|
+
title = titleMatch[1];
|
|
318
|
+
// Everything before the title is the author
|
|
319
|
+
const beforeTitle = fullText.substring(0, fullText.indexOf('"')).trim();
|
|
320
|
+
if (beforeTitle && beforeTitle !== ",") {
|
|
321
|
+
author = beforeTitle.replace(/,\s*$/, "").trim();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
// No quoted title, use the whole text as title
|
|
326
|
+
title = fullText.replace(/[.,]\s*$/, "").trim();
|
|
327
|
+
}
|
|
328
|
+
const customRef = {
|
|
329
|
+
anchor: slugify(anchor),
|
|
330
|
+
originalName: anchor,
|
|
331
|
+
title,
|
|
332
|
+
};
|
|
333
|
+
if (author)
|
|
334
|
+
customRef.author = author;
|
|
335
|
+
if (target)
|
|
336
|
+
customRef.target = target;
|
|
337
|
+
if (currentSection === "normative") {
|
|
338
|
+
customNormative.push(customRef);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
customInformative.push(customRef);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
normative: [...normative].sort((a, b) => parseInt(a, 10) - parseInt(b, 10)),
|
|
348
|
+
informative: [...informative].sort((a, b) => parseInt(a, 10) - parseInt(b, 10)),
|
|
349
|
+
customNormative,
|
|
350
|
+
customInformative,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function slugify(text) {
|
|
354
|
+
return text
|
|
355
|
+
.toLowerCase()
|
|
356
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
357
|
+
.replace(/^-|-$/g, "");
|
|
358
|
+
}
|
|
359
|
+
function extractAppendices(lines) {
|
|
360
|
+
const appendices = [];
|
|
361
|
+
let currentAppendix = null;
|
|
362
|
+
let currentSubsection = null;
|
|
363
|
+
let inCodeBlock = false;
|
|
364
|
+
for (let i = 0; i < lines.length; i++) {
|
|
365
|
+
const line = lines[i];
|
|
366
|
+
if (line.startsWith("```")) {
|
|
367
|
+
inCodeBlock = !inCodeBlock;
|
|
368
|
+
}
|
|
369
|
+
if (inCodeBlock) {
|
|
370
|
+
if (currentSubsection) {
|
|
371
|
+
currentSubsection.content.push(line);
|
|
372
|
+
}
|
|
373
|
+
else if (currentAppendix) {
|
|
374
|
+
currentAppendix.content.push(line);
|
|
375
|
+
}
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const appendixMatch = line.match(/^##\s+Appendix\s+([A-Z])[:.]?\s*(.+)?/i);
|
|
379
|
+
const subsectionMatch = line.match(/^###\s+([A-Z])\.(\d+)\.?\s+(.+)/i);
|
|
380
|
+
if (appendixMatch) {
|
|
381
|
+
const [, letter, name] = appendixMatch;
|
|
382
|
+
currentSubsection = null;
|
|
383
|
+
currentAppendix = {
|
|
384
|
+
anchor: slugify(name || `Appendix ${letter}`),
|
|
385
|
+
name: (name || `Appendix ${letter}`).trim(),
|
|
386
|
+
content: [],
|
|
387
|
+
children: [],
|
|
388
|
+
};
|
|
389
|
+
appendices.push(currentAppendix);
|
|
390
|
+
}
|
|
391
|
+
else if (subsectionMatch && currentAppendix) {
|
|
392
|
+
const [, _letter, _num, name] = subsectionMatch;
|
|
393
|
+
currentSubsection = {
|
|
394
|
+
anchor: slugify(name),
|
|
395
|
+
name: name.trim(),
|
|
396
|
+
content: [],
|
|
397
|
+
children: [],
|
|
398
|
+
};
|
|
399
|
+
currentAppendix.children.push(currentSubsection);
|
|
400
|
+
}
|
|
401
|
+
else if (line.startsWith("## ") && !line.includes("Appendix")) {
|
|
402
|
+
if (line.includes("Acknowledgements") || line.includes("Authors' Addresses")) {
|
|
403
|
+
currentAppendix = null;
|
|
404
|
+
currentSubsection = null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
if (currentSubsection) {
|
|
409
|
+
currentSubsection.content.push(line);
|
|
410
|
+
}
|
|
411
|
+
else if (currentAppendix) {
|
|
412
|
+
currentAppendix.content.push(line);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return appendices;
|
|
417
|
+
}
|
|
418
|
+
export function wrapBcp14Keywords(text) {
|
|
419
|
+
let result = text;
|
|
420
|
+
for (const keyword of BCP14_KEYWORDS) {
|
|
421
|
+
const regex = new RegExp(`(?<!<bcp14>)\\b${keyword}\\b(?!</bcp14>)`, "g");
|
|
422
|
+
result = result.replace(regex, `<bcp14>${keyword}</bcp14>`);
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
//# sourceMappingURL=Document.js.map
|