Skip to content

Commit

Permalink
fix(v2): modify api reference links since api.md is split (microsoft#34)
Browse files Browse the repository at this point in the history
* refactor main function

* fix api reference links
  • Loading branch information
arjunattam authored Dec 8, 2020
1 parent 4c40320 commit 247a5ff
Showing 1 changed file with 105 additions and 71 deletions.
176 changes: 105 additions & 71 deletions v2/scripts/fetchContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,6 @@ const slugify = require("slugify");
const sh = require("shelljs");
const semver = require("semver");

const { VERSION, SRC_DIR } = process.env;
if (!SRC_DIR) {
console.log("Use SRC_DIR to specify docs location, e.g. path-to-repo/docs.");
process.exit(1);
}
const srcDir = SRC_DIR;
const destDir = (function () {
return VERSION ? path.join("versioned_docs", `version-${VERSION}`) : "./docs";
})();
const sidebarFile = (function () {
return VERSION
? path.join("versioned_sidebars", `version-${VERSION}-sidebars.json`)
: "sidebars.js";
})();
const versionsFile = "versions.json";

function slugger(text) {
//@ts-ignore
return slugify(text, { lower: true, strict: true });
Expand Down Expand Up @@ -59,6 +43,24 @@ function closeTags(filePath) {
fse.writeFileSync(filePath, newContents);
}

function fixLinks(filePath, pagesForAPIHeadings) {
// TODO: this does not work when anchors slug is duplicate: e.g., page.$ and page.$$
// TODO: handle non-markdown links (e.g. dockerfile.bionic) -- redirect to github
const existingContents = fse.readFileSync(filePath).toString();
let contents = existingContents;
function replaceLink(contents, slug, newSlug) {
return contents.replace(slug, newSlug);
}
Object.keys(pagesForAPIHeadings).forEach((slug) => {
const fileName = pagesForAPIHeadings[slug];
// For inline links
contents = replaceLink(contents, `(#${slug})`, `(${fileName}#${slug})`);
// For links in footer
contents = replaceLink(contents, `api.md#${slug}`, `${fileName}#${slug}`);
});
fse.writeFileSync(filePath, contents);
}

function markdownFiles(dir) {
let files = [];
fse.readdirSync(dir).forEach((file) => {
Expand Down Expand Up @@ -86,28 +88,24 @@ function keepOnlyMarkdownFiles(dir) {
}

function overwriteFooterLinks(footerLines) {
// Changes
// #class-browsertype -> api.md#class-browsertype
return footerLines.map(line => {
return line.replace(/(#[^\s]+)/g, 'api.md$1');
});
// Changes relative to absolute
// e.g. #class-browsertype -> api.md#class-browsertype
return footerLines.map((line) => line.replace(/(#[^\s]+)/g, "api.md$1"));
}

function splitApi(contents) {
function splitApi(contents, destDir) {
const pageForHeading = {};
const tokens = md.parse(contents, {});
const headings = tokens.filter(
(t) => t.type === "heading_open" && t.tag === "h3"
);
const headings = tokens.filter((t) => t.type === "heading_open");
const headingsToSplitPages = headings.filter((t) => t.tag === "h3");
const lines = contents.split("\n");
// TODO: also fix the links since we are splitting api.md
// e.g., #class-browsertype -> api.md#class-browsertype -> api/class-browsertype.md
const tokensWithLineNum = tokens.filter(t => t.map);
const tokensWithLineNum = tokens.filter((t) => t.map);
const lastToken = tokensWithLineNum[tokensWithLineNum.length - 1];
const footerStart = lastToken.map[1];
const footerLines = overwriteFooterLinks(lines.slice(footerStart));

const lineNums = headings.map((h) => h.map[0]);
const pairs = lineNums.reduce(function (result, value, index, array) {
const lineNums = headingsToSplitPages.map((h) => h.map[0]);
const pairs = lineNums.reduce((result, value, index, array) => {
if (index < array.length - 1) {
result.push(array.slice(index, index + 2));
} else {
Expand All @@ -117,22 +115,35 @@ function splitApi(contents) {
}, []);

pairs.forEach((p) => {
const [start, end] = p;
fse.mkdirpSync(path.join(destDir, "api"));
const contentLines = [...lines.slice(p[0], p[1]), ...footerLines];
const contentLines = [...lines.slice(start, end), ...footerLines];
const contents = contentLines.join("\n");
const title = getTitle(contents);
const slug = slugger(title);
const filePath = path.join(destDir, "api", `${slug}.md`);
// promote headings by 2 levels
const relativePath = path.join("api", `${slug}.md`);
const fullPath = path.join(destDir, relativePath);
// Promote headings by 2 levels
const newContents = contents
.replace(/###/g, "#")
.replace(/####/g, "##")
.replace(/#####/g, "###");
fse.writeFileSync(filePath, newContents);
fse.writeFileSync(fullPath, newContents);

const pairHeadings = headings.filter(
(t) => t.map[0] >= start && t.map[0] <= end
);
const internalHeadings = pairHeadings
.map((t) => getTitle(lines[t.map[0]]))
.map((t) => slugger(t));
internalHeadings.forEach((h) => {
pageForHeading[h] = relativePath;
});
});
return pageForHeading;
}

function generateApiSidebar(contents) {
function generateApiSidebar(contents, version) {
// parse the table of contents
const tokens = md.parse(contents, {});
const ul = tokens.find((t) => t.type === "bullet_list_open");
Expand All @@ -143,7 +154,7 @@ function generateApiSidebar(contents) {
.map((li) => li.children.find((c) => c.type === "text"))
.map((t) => t.content)
.map(slugger);
const prefix = VERSION ? `version-${VERSION}/api/` : "api/";
const prefix = version ? `version-${version}/api/` : "api/";
return [
{
type: "category",
Expand All @@ -154,7 +165,7 @@ function generateApiSidebar(contents) {
];
}

function generateDocsSidebar(contents) {
function generateDocsSidebar(contents, version) {
// For pre-v1.2.0 tags, the sidebar does not have categories
// and is a flat list of links to docs.
const tokens = md.parse(contents, {});
Expand All @@ -166,7 +177,7 @@ function generateDocsSidebar(contents) {
t.map[1] <= ol.map[1] &&
t.level === 3
);
const prefix = VERSION ? `version-${VERSION}/` : "";
const prefix = version ? `version-${version}/` : "";

function headingContent(token) {
return token.children.find((t) => t.type === "text").content;
Expand Down Expand Up @@ -203,7 +214,7 @@ function generateDocsSidebar(contents) {
};
}

const hasCategories = !VERSION || !semver.lt(VERSION, "1.3.0");
const hasCategories = !version || !semver.lt(version, "1.3.0");
return hasCategories
? headings.map(subList).filter(Boolean)
: headings.map((h) => ({
Expand All @@ -213,23 +224,23 @@ function generateDocsSidebar(contents) {
}));
}

function writeSidebarFile(apiSidebar, docsSidebar) {
function writeSidebarFile(apiSidebar, docsSidebar, sidebarFile, version) {
const sidebar = {};
const docsKey = VERSION ? `version-${VERSION}/docs` : "docs";
const apiKey = VERSION ? `version-${VERSION}/api` : "api";
const docsKey = version ? `version-${version}/docs` : "docs";
const apiKey = version ? `version-${version}/api` : "api";
sidebar[docsKey] = docsSidebar;
sidebar[apiKey] = apiSidebar;
const content = VERSION
const content = version
? JSON.stringify(sidebar)
: `module.exports = ${JSON.stringify(sidebar)};`;
fse.ensureFileSync(sidebarFile);
fse.writeFileSync(sidebarFile, content);
}

function copyFiles() {
function copyFiles(srcDir, destDir, version) {
const currentDir = sh.pwd().stdout;
sh.cd(srcDir);
const tag = VERSION ? `tags/v${VERSION}` : `master`;
const tag = version ? `tags/v${version}` : `master`;
const result = sh.exec(`git checkout ${tag}`);
if (result.code !== 0) {
console.warn(`git checkout to ${tag} failed. Check source repo.`);
Expand All @@ -239,43 +250,66 @@ function copyFiles() {
fse.copySync(srcDir, destDir);
}

function writeVersionsFile() {
function writeVersionsFile(version) {
let newVersions = [];
const versionsFile = "versions.json";
if (fse.existsSync(versionsFile)) {
const versions = JSON.parse(fse.readFileSync("versions.json").toString());
versions.unshift(VERSION);
versions.unshift(version);
newVersions = versions;
} else {
newVersions = [VERSION];
newVersions = [version];
}
const uniqVersions = semver.rsort([...new Set(newVersions)]);
fse.writeFileSync("versions.json", JSON.stringify(uniqVersions));
}

// Main
fse.mkdirpSync(destDir);
fse.emptyDirSync(destDir);
copyFiles();
keepOnlyMarkdownFiles(destDir);

// Transform API reference
const api = fse.readFileSync(path.join(destDir, "api.md")).toString();
splitApi(api);
const apiSidebar = generateApiSidebar(api);
function main() {
const { VERSION, SRC_DIR } = process.env;
if (!SRC_DIR) {
console.log(
"Use SRC_DIR to specify docs location, e.g. path-to-repo/docs."
);
process.exit(1);
}
const srcDir = SRC_DIR;
const destDir = (function () {
return VERSION
? path.join("versioned_docs", `version-${VERSION}`)
: "./docs";
})();
const sidebarFile = (function () {
return VERSION
? path.join("versioned_sidebars", `version-${VERSION}-sidebars.json`)
: "sidebars.js";
})();
fse.mkdirpSync(destDir);
fse.emptyDirSync(destDir);
copyFiles(srcDir, destDir, VERSION);
keepOnlyMarkdownFiles(destDir);

// Transform markdown files
const files = markdownFiles(destDir);
files.forEach((filePath) => {
writeFrontmatter(filePath);
closeTags(filePath);
});
const docsSidebar = generateDocsSidebar(
fse.readFileSync(path.join(destDir, "README.md")).toString()
);
// Transform API reference
const apiContents = fse.readFileSync(path.join(destDir, "api.md")).toString();
const pagesForAPIHeadings = splitApi(apiContents, destDir);
const apiSidebar = generateApiSidebar(apiContents, VERSION);

// Create sidebar
writeSidebarFile(apiSidebar, docsSidebar);
// Transform markdown files
const files = markdownFiles(destDir);
files.forEach((filePath) => {
writeFrontmatter(filePath);
fixLinks(filePath, pagesForAPIHeadings);
closeTags(filePath);
});
// Build docs sidebar
const docsIndex = fse
.readFileSync(path.join(destDir, "README.md"))
.toString();
const docsSidebar = generateDocsSidebar(docsIndex, VERSION);
writeSidebarFile(apiSidebar, docsSidebar, sidebarFile, VERSION);

if (VERSION) {
writeVersionsFile();
if (VERSION) {
writeVersionsFile(VERSION);
}
}

main();

0 comments on commit 247a5ff

Please sign in to comment.