forked from microsoft/TypeScript-DOM-lib-generator
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfetcher.ts
More file actions
123 lines (112 loc) · 3.69 KB
/
Copy pathfetcher.ts
File metadata and controls
123 lines (112 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import * as fs from "fs";
import * as path from "path";
import fetch from "node-fetch";
import { JSDOM } from "jsdom";
fetchIDLs();
interface IDLSource {
url: string;
title: string;
deprecated?: boolean;
}
async function fetchIDLs() {
const idlSources = require("../inputfiles/idlSources.json") as IDLSource[];
await Promise.all(idlSources.map(async source => {
const { idl, comments } = await fetchIDL(source);
fs.writeFileSync(path.join(__dirname, `../inputfiles/idl/${source.title}.widl`), idl + '\n');
if (comments) {
fs.writeFileSync(path.join(__dirname, `../inputfiles/idl/${source.title}.commentmap.json`), comments + '\n');
}
}));
}
async function fetchIDL(source: IDLSource) {
const response = await fetch(source.url);
const dom = JSDOM.fragment(await response.text());
const elements = Array.from(dom.querySelectorAll("pre.idl:not(.extract):not(.example)"))
.filter(el => {
if (el.parentElement && el.parentElement.classList.contains("example")) {
return false;
}
const previous = el.previousElementSibling;
if (!previous) {
return true;
}
return !previous.classList.contains("atrisk") && !previous.textContent!.includes("IDL Index");
});
if (!elements.length) {
throw new Error("Found no IDL code");
}
const idl = elements.map(element => trimCommonIndentation(element.textContent!).trim()).join('\n\n');
const comments = processComments(dom);
return { idl, comments };
}
function processComments(dom: DocumentFragment) {
const elements = Array.from(dom.querySelectorAll("dl.domintro"));
if (!elements.length) {
return undefined;
}
const result: Record<string, string> = {};
for (const element of elements) {
let child = element.firstElementChild;
while (child) {
const key = getKey(child.innerHTML);
child = child.nextElementSibling;
const childKey = child && getKey(child.innerHTML);
if (key && child && (child === element.lastElementChild || !isNextKey(key, childKey))) {
result[key] = getCommentText(child.textContent!);
child = child.nextElementSibling;
}
}
}
return JSON.stringify(result, undefined, 4);
}
function isNextKey(k1: string, k2: string | null | undefined) {
return k2 && k1.split("-")[0] === k2.split("-")[0];
}
function getKey(s: string) {
const keyRegexp = /#dom-([a-zA-Z-_]+)/i;
const match = s.match(keyRegexp);
if (match) {
return match[1];
}
return undefined;
}
function getCommentText(text: string) {
return text
.replace(/’/g, "'")
.split("\n")
.map(line => line.trim())
.filter(line => !!line)
.map(line => line.slice(getIndentation(line))).join("\n");
}
/**
* Remove common indentation:
* <pre>
* typedef Type = "type";
* dictionary Dictionary {
* "member"
* };
* </pre>
* Here the textContent has 6 common preceding whitespaces that can be unindented.
*/
function trimCommonIndentation(text: string) {
const lines = text.split("\n");
if (!lines[0].trim()) {
lines.shift();
}
if (!lines[lines.length - 1].trim()) {
lines.pop();
}
const commonIndentation = Math.min(...lines.map(getIndentation));
return lines.map(line => line.slice(commonIndentation)).join("\n");
}
/** Count preceding whitespaces */
function getIndentation(line: string) {
let count = 0;
for (const ch of line) {
if (ch !== " ") {
break;
}
count++;
}
return count;
}