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
135 lines (123 loc) · 4.02 KB
/
Copy pathfetcher.ts
File metadata and controls
135 lines (123 loc) · 4.02 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
124
125
126
127
128
129
130
131
132
133
134
135
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;
}
const idlSelector = [
"pre.idl:not(.extract):not(.example)", // bikeshed and ReSpec
"pre.code code.idl-code", // Web Cryptography
"pre:not(.extract) code.idl" // HTML
].join(",");
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);
if (source.url.endsWith(".idl")) {
return { idl: await response.text() };
}
const dom = JSDOM.fragment(await response.text());
const elements = Array.from(dom.querySelectorAll(idlSelector))
.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 from ${source.url}`);
}
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 = 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;
}
}
}
if (!Object.keys(result).length) {
return undefined;
}
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;
}