���� JFIF �� � ( %"1"%)+...383,7(-.-
![]() Server : Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.4.20 System : Linux st2.domain.com 3.10.0-1127.10.1.el7.x86_64 #1 SMP Wed Jun 3 14:28:03 UTC 2020 x86_64 User : apache ( 48) PHP Version : 7.4.20 Disable Function : NONE Directory : /home/real/node-v13.0.1/tools/doc/ |
// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; const common = require('./common.js'); const fs = require('fs'); const getVersions = require('./versions.js'); const unified = require('unified'); const find = require('unist-util-find'); const visit = require('unist-util-visit'); const markdown = require('remark-parse'); const remark2rehype = require('remark-rehype'); const raw = require('rehype-raw'); const htmlStringify = require('rehype-stringify'); const path = require('path'); const typeParser = require('./type-parser.js'); module.exports = { toHTML, firstHeader, preprocessText, preprocessElements, buildToc }; const docPath = path.resolve(__dirname, '..', '..', 'doc'); // Add class attributes to index navigation links. function navClasses() { return (tree) => { visit(tree, { type: 'element', tagName: 'a' }, (node) => { node.properties.class = 'nav-' + node.properties.href.replace('.html', '').replace(/\W+/g, '-'); }); }; } const gtocPath = path.join(docPath, 'api', 'index.md'); const gtocMD = fs.readFileSync(gtocPath, 'utf8').replace(/^<!--.*?-->/gms, ''); const gtocHTML = unified() .use(markdown) .use(remark2rehype, { allowDangerousHTML: true }) .use(raw) .use(navClasses) .use(htmlStringify) .processSync(gtocMD).toString(); const templatePath = path.join(docPath, 'template.html'); const template = fs.readFileSync(templatePath, 'utf8'); async function toHTML({ input, content, filename, nodeVersion }) { filename = path.basename(filename, '.md'); const id = filename.replace(/\W+/g, '-'); let HTML = template.replace('__ID__', id) .replace(/__FILENAME__/g, filename) .replace('__SECTION__', content.section) .replace(/__VERSION__/g, nodeVersion) .replace('__TOC__', content.toc) .replace('__GTOC__', gtocHTML.replace( `class="nav-${id}`, `class="nav-${id} active`)) .replace('__EDIT_ON_GITHUB__', editOnGitHub(filename)) .replace('__CONTENT__', content.toString()); const docCreated = input.match( /<!--\s*introduced_in\s*=\s*v([0-9]+)\.([0-9]+)\.[0-9]+\s*-->/); if (docCreated) { HTML = HTML.replace('__ALTDOCS__', await altDocs(filename, docCreated)); } else { console.error(`Failed to add alternative version links to ${filename}`); HTML = HTML.replace('__ALTDOCS__', ''); } return HTML; } // Set the section name based on the first header. Default to 'Index'. function firstHeader() { return (tree, file) => { file.section = 'Index'; const heading = find(tree, { type: 'heading' }); if (heading) { const text = find(heading, { type: 'text' }); if (text) file.section = text.value; } }; } // Handle general body-text replacements. // For example, link man page references to the actual page. function preprocessText() { return (tree) => { visit(tree, null, (node) => { if (node.type === 'text' && node.value) { const value = linkJsTypeDocs(linkManPages(node.value)); if (value !== node.value) { node.type = 'html'; node.value = value; } } }); }; } // Syscalls which appear in the docs, but which only exist in BSD / macOS. const BSD_ONLY_SYSCALLS = new Set(['lchmod']); const LINUX_DIE_ONLY_SYSCALLS = new Set(['uname']); const HAXX_ONLY_SYSCALLS = new Set(['curl']); const MAN_PAGE = /(^|\s)([a-z.]+)\((\d)([a-z]?)\)/gm; // Handle references to man pages, eg "open(2)" or "lchmod(2)". // Returns modified text, with such refs replaced with HTML links, for example // '<a href="http://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>'. function linkManPages(text) { return text.replace( MAN_PAGE, (match, beginning, name, number, optionalCharacter) => { // Name consists of lowercase letters, // number is a single digit with an optional lowercase letter. const displayAs = `<code>${name}(${number}${optionalCharacter})</code>`; if (BSD_ONLY_SYSCALLS.has(name)) { return `${beginning}<a href="https://www.freebsd.org/cgi/man.cgi` + `?query=${name}&sektion=${number}">${displayAs}</a>`; } if (LINUX_DIE_ONLY_SYSCALLS.has(name)) { return `${beginning}<a href="https://linux.die.net/man/` + `${number}/${name}">${displayAs}</a>`; } if (HAXX_ONLY_SYSCALLS.has(name)) { return `${beginning}<a href="https://${name}.haxx.se/docs/manpage.html">${displayAs}</a>`; } return `${beginning}<a href="http://man7.org/linux/man-pages/man${number}` + `/${name}.${number}${optionalCharacter}.html">${displayAs}</a>`; }); } const TYPE_SIGNATURE = /\{[^}]+\}/g; function linkJsTypeDocs(text) { const parts = text.split('`'); // Handle types, for example the source Markdown might say // "This argument should be a {number} or {string}". for (let i = 0; i < parts.length; i += 2) { const typeMatches = parts[i].match(TYPE_SIGNATURE); if (typeMatches) { typeMatches.forEach((typeMatch) => { parts[i] = parts[i].replace(typeMatch, typeParser.toLink(typeMatch)); }); } } return parts.join('`'); } // Preprocess headers, stability blockquotes, and YAML blocks. function preprocessElements({ filename }) { return (tree) => { const STABILITY_RE = /(.*:)\s*(\d)([\s\S]*)/; let headingIndex = -1; let heading = null; visit(tree, null, (node, index) => { if (node.type === 'heading') { headingIndex = index; heading = node; } else if (node.type === 'html' && common.isYAMLBlock(node.value)) { node.value = parseYAML(node.value); } else if (node.type === 'blockquote') { const paragraph = node.children[0].type === 'paragraph' && node.children[0]; const text = paragraph && paragraph.children[0].type === 'text' && paragraph.children[0]; if (text && text.value.includes('Stability:')) { const [, prefix, number, explication] = text.value.match(STABILITY_RE); const isStabilityIndex = index - 2 === headingIndex || // General. index - 3 === headingIndex; // With api_metadata block. if (heading && isStabilityIndex) { heading.stability = number; headingIndex = -1; heading = null; } // Do not link to the section we are already in. const noLinking = filename.includes('documentation') && heading !== null && heading.children[0].value === 'Stability Index'; // Collapse blockquote and paragraph into a single node node.type = 'paragraph'; node.children.shift(); node.children.unshift(...paragraph.children); // Insert div with prefix and number node.children.unshift({ type: 'html', value: `<div class="api_stability api_stability_${number}">` + (noLinking ? '' : '<a href="documentation.html#documentation_stability_index">') + `${prefix} ${number}${noLinking ? '' : '</a>'}` .replace(/\n/g, ' ') }); // Remove prefix and number from text text.value = explication; // close div node.children.push({ type: 'html', value: '</div>' }); } } }); }; } function parseYAML(text) { const meta = common.extractAndParseYAML(text); let result = '<div class="api_metadata">\n'; const added = { description: '' }; const deprecated = { description: '' }; const removed = { description: '' }; if (meta.added) { added.version = meta.added.join(', '); added.description = `<span>Added in: ${added.version}</span>`; } if (meta.deprecated) { deprecated.version = meta.deprecated.join(', '); deprecated.description = `<span>Deprecated since: ${deprecated.version}</span>`; } if (meta.removed) { removed.version = meta.removed.join(', '); removed.description = `<span>Removed in: ${removed.version}</span>`; } if (meta.changes.length > 0) { if (added.description) meta.changes.push(added); if (deprecated.description) meta.changes.push(deprecated); if (removed.description) meta.changes.push(removed); meta.changes.sort((a, b) => versionSort(a.version, b.version)); result += '<details class="changelog"><summary>History</summary>\n' + '<table>\n<tr><th>Version</th><th>Changes</th></tr>\n'; meta.changes.forEach((change) => { const description = unified() .use(markdown) .use(remark2rehype, { allowDangerousHTML: true }) .use(raw) .use(htmlStringify) .processSync(change.description).toString(); const version = common.arrify(change.version).join(', '); result += `<tr><td>${version}</td>\n` + `<td>${description}</td></tr>\n`; }); result += '</table>\n</details>\n'; } else { result += `${added.description}${deprecated.description}` + `${removed.description}\n`; } if (meta.napiVersion) { result += `<span>N-API version: ${meta.napiVersion.join(', ')}</span>\n`; } result += '</div>'; return result; } function minVersion(a) { return common.arrify(a).reduce((min, e) => { return !min || versionSort(min, e) < 0 ? e : min; }); } const numberRe = /^\d*/; function versionSort(a, b) { a = minVersion(a).trim(); b = minVersion(b).trim(); let i = 0; // Common prefix length. while (i < a.length && i < b.length && a[i] === b[i]) i++; a = a.substr(i); b = b.substr(i); return +b.match(numberRe)[0] - +a.match(numberRe)[0]; } function buildToc({ filename, apilinks }) { return (tree, file) => { const startIncludeRefRE = /^\s*<!-- \[start-include:(.+)\] -->\s*$/; const endIncludeRefRE = /^\s*<!-- \[end-include:.+\] -->\s*$/; const realFilenames = [filename]; const idCounters = Object.create(null); let toc = ''; let depth = 0; visit(tree, null, (node) => { // Keep track of the current filename for comment wrappers of inclusions. if (node.type === 'html') { const [, includedFileName] = node.value.match(startIncludeRefRE) || []; if (includedFileName !== undefined) realFilenames.unshift(includedFileName); else if (endIncludeRefRE.test(node.value)) realFilenames.shift(); } if (node.type !== 'heading') return; if (node.depth - depth > 1) { throw new Error( `Inappropriate heading level:\n${JSON.stringify(node)}` ); } depth = node.depth; const realFilename = path.basename(realFilenames[0], '.md'); const headingText = file.contents.slice( node.children[0].position.start.offset, node.position.end.offset).trim(); const id = getId(`${realFilename}_${headingText}`, idCounters); const hasStability = node.stability !== undefined; toc += ' '.repeat((depth - 1) * 2) + (hasStability ? `* <span class="stability_${node.stability}">` : '* ') + `<a href="#${id}">${headingText}</a>${hasStability ? '</span>' : ''}\n`; let anchor = `<span><a class="mark" href="#${id}" id="${id}">#</a></span>`; if (realFilename === 'errors' && headingText.startsWith('ERR_')) { anchor += `<span><a class="mark" href="#${headingText}" ` + `id="${headingText}">#</a></span>`; } const api = headingText.replace(/^.*:\s+/, '').replace(/\(.*/, ''); if (apilinks[api]) { anchor = `<a class="srclink" href=${apilinks[api]}>[src]</a>${anchor}`; } node.children.push({ type: 'html', value: anchor }); }); file.toc = unified() .use(markdown) .use(remark2rehype, { allowDangerousHTML: true }) .use(raw) .use(htmlStringify) .processSync(toc).toString(); }; } const notAlphaNumerics = /[^a-z0-9]+/g; const edgeUnderscores = /^_+|_+$/g; const notAlphaStart = /^[^a-z]/; function getId(text, idCounters) { text = text.toLowerCase() .replace(notAlphaNumerics, '_') .replace(edgeUnderscores, '') .replace(notAlphaStart, '_$&'); if (idCounters[text] !== undefined) { return `${text}_${++idCounters[text]}`; } idCounters[text] = 0; return text; } async function altDocs(filename, docCreated) { const [, docCreatedMajor, docCreatedMinor] = docCreated.map(Number); const host = 'https://nodejs.org'; const versions = await getVersions.versions(); const getHref = (versionNum) => `${host}/docs/latest-v${versionNum}/api/${filename}.html`; const wrapInListItem = (version) => `<li><a href="${getHref(version.num)}">${version.num}` + `${version.lts ? ' <b>LTS</b>' : ''}</a></li>`; function isDocInVersion(version) { const [versionMajor, versionMinor] = version.num.split('.').map(Number); if (docCreatedMajor > versionMajor) return false; if (docCreatedMajor < versionMajor) return true; if (Number.isNaN(versionMinor)) return true; return docCreatedMinor <= versionMinor; } const list = versions.filter(isDocInVersion).map(wrapInListItem).join('\n'); return list ? ` <li class="version-picker"> <a href="#">View another version <span>▼</span></a> <ol class="version-picker">${list}</ol> </li> ` : ''; } // eslint-disable-next-line max-len const githubLogo = '<span class="github_icon"><svg height="16" width="16" viewBox="0 0 16.1 16.1" fill="currentColor"><path d="M8 0a8 8 0 0 0-2.5 15.6c.4 0 .5-.2.5-.4v-1.5c-2 .4-2.5-.5-2.7-1 0-.1-.5-.9-.8-1-.3-.2-.7-.6 0-.6.6 0 1 .6 1.2.8.7 1.2 1.9 1 2.4.7 0-.5.2-.9.5-1-1.8-.3-3.7-1-3.7-4 0-.9.3-1.6.8-2.2 0-.2-.3-1 .1-2 0 0 .7-.3 2.2.7a7.4 7.4 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.5 1.1.2 2 .1 2.1.5.6.8 1.3.8 2.2 0 3-1.9 3.7-3.6 4 .3.2.5.7.5 1.4v2.2c0 .2.1.5.5.4A8 8 0 0 0 16 8a8 8 0 0 0-8-8z"/></svg></span>'; function editOnGitHub(filename) { return `<li class="edit_on_github"><a href="https://github.com/nodejs/node/edit/master/doc/api/${filename}.md">${githubLogo}Edit on GitHub</a></li>`; }