preview/core.js (164 lines of code) (raw):

/** * @license * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ 'use strict'; const bufferItr = require("./buffer_itr"); const fs = require("fs"); const dedent = require("dedent"); const Git = require("./git"); const path = require("path"); const { Readable } = require("stream"); const { promisify } = require('util'); const stat = promisify(fs.stat); const readFile = promisify(fs.readFile); const GitCore = (defaultTemplate, ignoreHost, repoPath) => { const hostInfo = hostPrefix => { if (ignoreHost || !hostPrefix) { return [defaultTemplate, "master"]; } let prefix = hostPrefix; let template; if (prefix.startsWith("gapped_")) { template = "air_gapped_template.html"; prefix = prefix.substring("gapped_".length); } else { template = "template.html"; } return [template, prefix]; }; const git = Git(repoPath); return hostPrefix => { const [templateName, branch] = hostInfo(hostPrefix); return { diff: () => Readable.from(bufferItr(diffItr(git, branch), 16 * 1024)), outsideOfGuide: path => { if (templateName === "air_gapped_template.html") { return null; } return "https://www.elastic.co" + path; }, redirects: () => git.catBlob(`${branch}:redirects.conf`), file: async requestedPath => { const templateExists = await hasTemplate(git, branch); /* * We're using the existence of the template to check if the branch is * "ready" for templating on the fly. There are a few changes that came in * with that preparation, like making sure all of the required js is written * to the `raw` directory as well. */ const pathPrefix = templateExists ? "raw" : "html"; const requestedObject = `${branch}:${pathPrefix}${requestedPath}`; const objectType = await git.objectType(requestedObject); switch (objectType) { case "missing": return "missing"; case "tree": return "dir"; case 'blob': const dir = path.dirname(requestedObject); return { hasTemplate: templateExists, stream: git.catBlob(requestedObject), template: () => git.catBlob(`${branch}:${templateName}`), lang: () => git.catBlobToString(`${dir}/lang`), alternativesReport: () => git.catBlobToString( `${dir}/alternatives_summary.json`, 10 * 1024 ), }; default: throw new Error(`Don't know how to return ${objecType}`); } }, }; }; }; const FsCore = (defaultTemplate, ignoreHost, rootPath) => { const hostInfo = hostPrefix => { if (ignoreHost || !hostPrefix) { return defaultTemplate; } return "gapped" === hostPrefix ? "air_gapped_template.html" : "template.html"; }; return hostPrefix => { const template = hostInfo(hostPrefix); const readAlternativesReport = async dir => { try { return await readFile(`${dir}alternatives_summary.json`, { encoding: "utf8" }); } catch (err) { if (err.code === "ENOENT") { throw "missing"; } throw err; } }; return { diff: () => { const r = new Readable; r.push("diff not supported"); r.push(null); return r; }, outsideOfGuide: _path => null, redirects: () => null, file: async requestedPath => { const realPath = `${rootPath}/${requestedPath}`; let pathStat; try { pathStat = await stat(realPath); } catch (err) { if (err.code === "ENOENT") { return "missing"; } throw err; } if (pathStat.isDirectory()) { return "dir"; } const dir = path.dirname(realPath); return { hasTemplate: true, stream: fs.createReadStream(realPath), template: () => fs.createReadStream(`/docs_build/resources/web/${template}`), lang: () => readFile(`${dir}lang`, { encoding: "utf8" }), alternativesReport: () => readAlternativesReport(dir), }; } }; }; }; module.exports = { Git: GitCore, Fs: FsCore, }; const hasTemplate = async (git, branch) => { const type = await git.objectType(`${branch}:template.html`); switch (type) { case 'blob': return true; case 'missing': return false; default: throw new Error(`The template is a strange object type: ${type}`); } } /** * Creates an async iterator describing the diff. The iterator is very "chatty" * so is probably best wrapped in bufferItr. * @param {string} branch branch to describe */ const diffItr = async function* (git, branch) { yield dedent ` <!DOCTYPE html> <html> <head> <title>Diff for ${branch}</title> </head> <body><ul>\n`; let sawAny = false; for await (const change of git.diffLastCommit(branch)) { // Skip boring files if (!change.path.startsWith("raw/")) { continue; } // Strip the prefixes from the paths change.path = change.path.substring("raw/".length); if (change.movedToPath) { change.movedToPath = change.movedToPath.substring("raw/".length); } // Build the output html yield ` <li>+${change.added} -${change.removed}`; const linkTarget = change.movedToPath ? change.movedToPath : change.path; yield ` <a href="/guide/${linkTarget}">`; yield change.movedToPath ? `${change.path} -> ${change.movedToPath}` : change.path; yield `</a>\n`; sawAny = true; } yield `</ul>`; if (!sawAny) { yield `<p>There aren't any differences!</p>`; } yield `</html>\n`; };