Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 62 additions & 20 deletions components/index.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,109 @@
// eslint-disable-next-line no-unused-vars
import { AsyncAPIDocumentInterface } from '@asyncapi/parser';
import { includeFile, generateBase64Favicon, renderSpec, stringifySpec, stringifyConfiguration } from '../helpers/all';
import { AsyncAPIDocumentInterface } from "@asyncapi/parser";
import {
includeFile,
generateBase64Favicon,
renderSpec,
stringifySpec,
stringifyConfiguration,
} from "../helpers/all";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function copyAsset(srcRel, destRel, outputDir) {
const src = path.join(__dirname, srcRel);
const dest = path.join(outputDir, destRel);
const destDir = path.dirname(dest);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
fs.copyFileSync(src, dest);
}

/**
* @param {{asyncapi: AsyncAPIDocumentInterface, params: any}} param0
* @param {{asyncapi: AsyncAPIDocumentInterface, params: any}} param0
*/
export function Index({ asyncapi, params = {} }) {
const outputDir = params.outputDir || process.cwd();
const favicon = generateBase64Favicon(params);
const renderedSpec = renderSpec(asyncapi, params);
let asyncapiScript = `<script src="js/asyncapi-ui.min.js" type="application/javascript"></script>`;
// coerce singleFile param to bool, or "false" string will be true
const singleFile = (params?.singleFile === true || params?.singleFile == 'true');
if(singleFile) {
const singleFile =
params?.singleFile === true || params?.singleFile == "true";
if (singleFile) {
asyncapiScript = `<script type="text/javascript">
${includeFile('template/js/asyncapi-ui.min.js')}
${includeFile("template/js/asyncapi-ui.min.js")}
</script>`;
} else {
copyAsset(
"../template/js/asyncapi-ui.min.js",
"js/asyncapi-ui.min.js",
outputDir
);
}

let styling = `<link href="css/global.min.css" rel="stylesheet">
<link href="css/asyncapi.min.css" rel="stylesheet">`;
if(singleFile) {
if (singleFile) {
styling = `<style type="text/css">
${includeFile("template/css/global.min.css")}
${includeFile("template/css/asyncapi.min.css")}
</style>`;
} else {
copyAsset(
"../template/css/global.min.css",
"css/global.min.css",
outputDir
);
copyAsset(
"../template/css/asyncapi.min.css",
"css/asyncapi.min.css",
outputDir
);
}
let basehref = '';
if(params.baseHref) {

let basehref = "";
if (params.baseHref) {
basehref = `<base href="${params.baseHref}">`;
}
let appJs = `<script type="application/javascript" src="js/app.js"></script>`;
if(singleFile) {
appJs = `<script>${App({asyncapi, params})}</script>`;
if (singleFile) {
appJs = `<script>${App({ asyncapi, params })}</script>`;
} else {
copyAsset("../template/js/app.js", "js/app.js", outputDir);
}
return (`<!DOCTYPE html>
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
${basehref}
<title>${asyncapi.info().title()} ${asyncapi.info().version()} documentation</title>
<title>${asyncapi.info().title()} ${asyncapi
.info()
.version()} documentation</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="${favicon}" />
${styling}
</head>

<body>
<div id="root">${renderedSpec}</div>

${asyncapiScript}

${appJs}
</body>
</html>`
);
</html>`;
}

export function App({ asyncapi, params = {} }) {
return (`
return `
const schema = ${stringifySpec(asyncapi)};
const config = ${stringifyConfiguration(params)};
const appRoot = document.getElementById('root');
AsyncApiStandalone.render(
{ schema, config, }, appRoot
);
`
);
`;
}
81 changes: 52 additions & 29 deletions helpers/all.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
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';
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);
return o && typeof o === "object" && !Array.isArray(o);
}






/**
* Performs a recursive deep merge while assuming only simple JSON types are used.
*/
Expand All @@ -33,7 +38,10 @@ function mergeInto(from, to) {
* Prepares configuration for component.
*/
export function prepareConfiguration(params = {}) {
const config = { show: { sidebar: true }, sidebar: { showOperations: 'byDefault' } };
const config = {
show: { sidebar: true },
sidebar: { showOperations: "byDefault" },
};
// Apply config override
if (params.config) {
let configOverride;
Expand All @@ -55,10 +63,10 @@ export function prepareConfiguration(params = {}) {
}
}
// Apply explicit config properties
if (params.sidebarOrganization === 'byTags') {
config.sidebar.showOperations = 'bySpecTags';
} else if (params.sidebarOrganization === 'byTagsNoRoot') {
config.sidebar.showOperations = 'byOperationsTags';
if (params.sidebarOrganization === "byTags") {
config.sidebar.showOperations = "bySpecTags";
} else if (params.sidebarOrganization === "byTagsNoRoot") {
config.sidebar.showOperations = "byOperationsTags";
}
return config;
}
Expand All @@ -77,12 +85,17 @@ export function loadLanguagesConfig() {
* 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 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', '')));
const lang = require(path.resolve(
hljsLanguagesPath,
langPath.replace(".js", "")
));
hljs.registerLanguage(lang.name, lang);
}

Expand All @@ -97,15 +110,21 @@ export function generateBase64Favicon(params) {

// generate Base64 of AsyncAPI logo
if (!favicon) {
return "data:image/x-icon;base64," + fs.readFileSync(path.resolve(__dirname, '../assets/asyncapi-favicon.ico'), "base64");
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');
const buffer = response.buffer();
return "data:image/x-icon;base64," + buffer.toString("base64");
}
} catch (fetchErr) {
// Failed to fetch favicon...
Expand All @@ -124,7 +143,7 @@ export function generateBase64Favicon(params) {
* 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);
const pathToFile = path.resolve(__dirname, "../", pathFile);
return fs.readFileSync(pathToFile);
}

Expand All @@ -134,8 +153,11 @@ export function includeFile(pathFile) {
*/
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
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;
}

/**
Expand All @@ -149,22 +171,23 @@ export function stringifyConfiguration(params) {
* Renders AsyncApi component by given AsyncAPI spec and with corresponding template configuration.
*/


/**
* @param {AsyncAPIDocumentInterface} asyncapi
* @param {*} params
* @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>');
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);
}
65 changes: 0 additions & 65 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading