/
generateTOC.js
176 lines (157 loc) · 4.87 KB
/
generateTOC.js
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// @ts-check
const { existsSync } = require("fs");
const { join, dirname, basename} = require("path");
const { writeFileSync } = require("fs");
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
/** Retrieve file paths from a given folder and its subfolders. */
// https://gist.github.com/kethinov/6658166#gistcomment-2936675
const getFilePaths = folderPath => {
const entryPaths = fs.readdirSync(folderPath).map(entry => path.join(folderPath, entry));
const filePaths = entryPaths.filter(entryPath => fs.statSync(entryPath).isFile());
const dirPaths = entryPaths.filter(entryPath => !filePaths.includes(entryPath));
const dirFiles = dirPaths.reduce((prev, curr) => prev.concat(getFilePaths(curr)), []);
return [...filePaths, ...dirFiles];
};
/**
* @typedef {Object} Item - an item in the TOC
* @property {string[]} path - the path to get to this file
* @property {string} name - the filename
* @property {string} id - an id for the slug
* @property {string} title - name
* @property {number} sortIndex - when listing the objects
* @property {string} hash - the md5 of the content
* @property {any} compilerSettings - name
*/
// * @property {string} body - the text for the example
const root = join(__dirname, "..", "en");
const categories = fs.readdirSync(root).filter(path => !path.startsWith(".") && !path.includes("."))
const allExampleFiles = categories.map(c => getFilePaths(join(root, c)))
/** @type {string[]} */
const all = [].concat.apply([], allExampleFiles)
.filter(p => p.endsWith(".ts") || p.endsWith(".tsx") || p.endsWith(".js"));
const examples = all.map(m => {
let contents = fs.readFileSync(m, "utf8");
const relative = path.relative(root, m);
const title = path
.basename(m)
.split(".")
.slice(0, -1)
.join(".");
let compiler = {};
let index = 1;
if (contents.startsWith("//// {")) {
const preJSON = contents.split("//// {")[1].split("}\n")[0];
contents = contents
.split("\n")
.slice(1)
.join("\n");
const code = "({" + preJSON + "})";
try {
const obj = eval(code);
if (obj.order) {
index = obj.order;
delete obj.order;
}
compiler = obj.compiler;
} catch (err) {
console.error(">>>> " + m);
console.error("Issue with: ", code);
throw err;
}
}
/** @type Item */
const item = {
path: dirname(relative).split("/"),
title: title,
name: basename(relative),
id: title
.toLowerCase()
.replace(/[^\x00-\x7F]/g, "-")
.replace(/ /g, "-")
.replace(/\//g, "-")
.replace(/\+/g, "-"),
// body: contents,
sortIndex: index,
hash: crypto
.createHash("md5")
.update(contents)
.digest("hex"),
compilerSettings: compiler
};
return item;
});
const toc = {
sections: [{
name: "JavaScript",
subtitle: "See how TypeScript improves day to day working with JavaScript with minimal additional syntax."
},
{
name: "TypeScript",
subtitle: "Explore how TypeScript extends JavaScript to add more safety and tooling."
},
{
name: "3.8",
subtitle: "See the <a href='https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/'>Beta notes</a>.",
whatisnew: true
},
{
name: "3.7",
subtitle: "See the <a href='https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/'>Release notes</a>.",
whatisnew: true
},
{
name: "Playground",
subtitle: "Learn what has changed in this website.",
whatisnew: true
}],
sortedSubSections: [
// JS
"JavaScript Essentials",
"Functions with JavaScript",
"Working With Classes",
"Modern JavaScript",
"External APIs",
"Helping with JavaScript",
// TS
"Primitives",
"Type Primitives",
"Meta-Types",
"Language",
"Language Extensions",
// Examples
"Syntax and Messaging",
"Types and Code Flow",
"Fixits",
// Playground
"Config",
"Tooling",
// 3.8
"Breaking Changes",
"JSDoc Improvements"
],
examples
}
validateTOC(toc)
const prodTableOfContentsFile = join(__dirname, "..", "..", "site/examplesTOC.json");
const devTableOfContentsFile = join(__dirname, "..", "..", "serve/examplesTOC.json");
if (existsSync( join(__dirname, "..", "..", "site"))) {
writeFileSync(prodTableOfContentsFile, JSON.stringify(toc));
}
if (existsSync( join(__dirname, "..", "..", "serve"))) {
writeFileSync(devTableOfContentsFile, JSON.stringify(toc));
}
function validateTOC(toc) {
// Ensure all subfolders are in the sorted section
const allSubFolders = []
all.forEach(path => {
const subPath = dirname(path).split("/").pop()
if (!allSubFolders.includes(subPath)){ allSubFolders.push(subPath) }
});
allSubFolders.forEach(s => {
if(!toc.sortedSubSections.includes(s)) {
throw new Error("Expected '" + s + "' in toc - " + toc.sortedSubSections.join(", "))
}
})
}