UNPKG

@asyncapi/html-template

Version:

HTML template for the AsyncAPI generator.

171 lines (153 loc) 5.26 kB
import path from 'path'; import fs from 'fs'; import ReactDOMServer from 'react-dom/server'; import fetch from 'sync-fetch'; import AsyncApiComponent, { hljs } from '@asyncapi/react-component'; import { AsyncAPIDocumentInterface, stringify } from '@asyncapi/parser'; function isJsonObject(o) { return o && typeof o === 'object' && !Array.isArray(o); } /** * Performs a recursive deep merge while assuming only simple JSON types are used. */ function mergeInto(from, to) { for (const key in from) { if (!Object.hasOwn(from, key)) { continue; } if (isJsonObject(from[key])) { if (!isJsonObject(to[key])) { to[key] = {}; } mergeInto(from[key], to[key]); } else { // Override with non-object JSON value to[key] = from[key]; } } } /** * Prepares configuration for component. */ export function prepareConfiguration(params = {}) { const config = { show: { sidebar: true }, sidebar: { showOperations: 'byDefault' } }; // Apply config override if (params.config) { let configOverride; try { // Attempt to parse inline stringified JSON configOverride = JSON.parse(params.config); } catch (jsonErr) { // Failed to parse JSON string... try { // Attempt to read as JSON file and parse contents configOverride = JSON.parse(fs.readFileSync(params.config, "utf8")); } catch (err) { console.error("Failed to parse config override JSON", jsonErr, err); throw err; } } if (isJsonObject(configOverride)) { mergeInto(configOverride, config); } } // Apply explicit config properties if (params.sidebarOrganization === 'byTags') { config.sidebar.showOperations = 'bySpecTags'; } else if (params.sidebarOrganization === 'byTagsNoRoot') { config.sidebar.showOperations = 'byOperationsTags'; } return config; } let initLanguages = false; /** * Load all language configurations from highlight.js */ export function loadLanguagesConfig() { if (initLanguages === true) { return; } /** * Retrieve the location of highlight.js. * It's needed because someone can have installed `highlight.js` as global dependency * or depper than local `node_modules` of this template. */ const hljsPackageDir = path.dirname(require.resolve("highlight.js/package.json")) const hljsLanguagesPath = path.resolve(hljsPackageDir, 'lib/languages'); const languages = fs.readdirSync(hljsLanguagesPath); for (let langPath of languages) { const lang = require(path.resolve(hljsLanguagesPath, langPath.replace('.js', ''))); hljs.registerLanguage(lang.name, lang); } initLanguages = true; } /** * Generate Base64 value from favicon */ export function generateBase64Favicon(params) { const favicon = params.favicon; // generate Base64 of AsyncAPI logo if (!favicon) { return "data:image/x-icon;base64," + fs.readFileSync(path.resolve(__dirname, '../assets/asyncapi-favicon.ico'), "base64"); } try { // Attempt to fetch favicon const response = fetch(favicon); if (response.status == 200) { const buffer = response.buffer() return "data:image/x-icon;base64," + buffer.toString('base64'); } } catch (fetchErr) { // Failed to fetch favicon... try { // Attempt to read favicon as file return "data:image/x-icon;base64," + fs.readFileSync(favicon, "base64"); } catch (err) { console.error("Failed to fetch/read favicon", fetchErr, err); throw err; } } } /** * More safe function to include content of given file than default Nunjuck's `include`. * Attaches raw file's content instead of executing it - problem with some attached files in template. */ export function includeFile(pathFile) { const pathToFile = path.resolve(__dirname, '../', pathFile); return fs.readFileSync(pathToFile); } /** * Stringifies the specification with escaping circular refs * and annotates that specification is parsed. */ export function stringifySpec(asyncapi) { const stringifiedDoc = stringify(asyncapi); if(stringifiedDoc === undefined) throw new Error("Unable to stringify parsed AsyncAPI document passed by the generator. Please report an issue in https://github.com/asyncapi/html-template repository.") return stringifiedDoc } /** * Stringifies prepared configuration for component. */ export function stringifyConfiguration(params) { return JSON.stringify(prepareConfiguration(params)); } /** * Renders AsyncApi component by given AsyncAPI spec and with corresponding template configuration. */ /** * @param {AsyncAPIDocumentInterface} asyncapi * @param {*} params */ export function renderSpec(asyncapi, params) { loadLanguagesConfig(); const config = prepareConfiguration(params); const stringified = stringifySpec(asyncapi); const component = <AsyncApiComponent schema={stringified} config={config}/>; if (typeof global.window === 'undefined' || !global.window.document) { const { JSDOM } = require('jsdom'); const jsdomInstance = new JSDOM('<!doctype html><html><body></body></html>'); global.window = jsdomInstance.window; global.document = jsdomInstance.window.document; } return ReactDOMServer.renderToString(component); }