module.exports = { friendlyName: 'To HTML', description: 'Compile a Markdown string into an HTML string.', extendedDescription: 'Expects GitHub-flavored Markdown syntax. Uses [`marked`](https://github.com/chjj/marked)@v0.3.5. '+ 'Inspired by https://github.com/mikermcneil/machinepack-markdown/tree/5d8cee127e8ce45c702ec9bbb2b4f9bc4b7fafac', moreInfoUrl: 'https://help.github.com/articles/basic-writing-and-formatting-syntax/', sideEffects: 'cacheable', inputs: { mdString: { description: 'Markdown string to convert', example: '# hello world\n it\'s me, some markdown string \n\n ```js\n//but maybe i have code snippets too...\n```', required: true }, allowHtml: { friendlyName: 'Allow HTML?', description: 'Whether or not to allow HTML tags in the Markdown input. Defaults to `true`.', extendedDescription: 'If `false`, any input that contains HTML tags will trigger the `unsafeMarkdown` exit.', example: true, defaultsTo: true }, addIdsToHeadings: { friendlyName: 'Add IDs to headings?', description: 'Whether or not to add an ID attribute to rendered heading tags like

', extendedDescription: 'This is not part of the Markdown specification (see http://daringfireball.net/projects/markdown/dingus), but it is the default behavior for the `marked` module. Defaults to `true`.', example: true, defaultsTo: true } }, exits: { success: { outputFriendlyName: 'HTML', outputExample: '

hello world

\n

it's me, some markdown string

\n
//but maybe i have code snippets too...
\n' }, unsafeMarkdown: { friendlyName: 'Unsafe Markdown detected', description: 'The provided input contained unsafe content (like HTML tags).' } }, fn: function(inputs, exits) { const { marked } = require('marked'); // For full list of options, see: // • https://github.com/chjj/marked var markedOpts = { gfm: true, tables: true, breaks: false, pedantic: false, smartLists: true, smartypants: false, }; let customRenderer = new marked.Renderer(); if (inputs.addIdsToHeadings === true) { var headingsRenderedOnThisPage = []; customRenderer.heading = function (text, level) { // If the heading has underscores and no spaces (e.g. osquery_async_host_collect_log_stats_interval) we'll add optional linebreaks before each underscore var textWithLineBreaks; if(text.match(/\S(\w+\_\S)+(\w\S)+/g) && !text.match(/\s/g)){ textWithLineBreaks = text.replace(/(\_)/g, '​_'); } var headingID = _.kebabCase(_.unescape(text.toLowerCase()).replace(/[\’\']/g, '')); if(!_.contains(headingsRenderedOnThisPage, headingID)){ headingsRenderedOnThisPage.push(headingID); } else { headingID = sails.helpers.strings.ensureUniq(headingID, headingsRenderedOnThisPage); headingsRenderedOnThisPage.push(headingID); } return ''+(textWithLineBreaks ? textWithLineBreaks : text)+'\n'; }; } else { customRenderer.heading = function (text, level) { var textWithLineBreaks; if(text.match(/\S(\w+\_\S)+(\w\S)+/g) && !text.match(/\s/g)){ textWithLineBreaks = text.replace(/(\_)/g, '​_'); } return ''+(textWithLineBreaks ? textWithLineBreaks : text)+''; }; } // Creating a custom codeblock renderer function to add syntax highlighting keywords and render mermaid code blocks (```mermaid```) without the added
 tags.
    customRenderer.code = function(code, infostring) {
      if(infostring === 'mermaid') {
        return `${_.escape(code)}`;
      } else if(infostring === 'js') {// Interpret `js` as `javascript`
        return `
${_.escape(code)}
`; } else if(infostring === 'bash' || infostring === 'sh') {// Interpret `sh` and `bash` as `bash` return `
${_.escape(code)}
`; } else if(infostring !== '') {// leaving the code language as-is if the infoString is anything else. return `
${_.escape(code)}
`; } else {// When unspecified, default to `text` return `
${_.escape(code)}
`; } }; // Creating a custom blockquote renderer function to render blockquotes as tip blockquotes. customRenderer.blockquote = function(blockquoteHtml) { return `
An icon indicating that this section has important information
\n${blockquoteHtml}\n
`; }; // Custom renderer function to enable checkboxes in Markdown lists. customRenderer.listitem = function(innerHtml, hasCheckbox, isChecked) { if(!hasCheckbox){ // « If a list item does not have a checkbox, we'll render it normally. return `
  • ${innerHtml}
  • `; } else if(isChecked) {// If this checkbox was checked in Markdown (- [x]), we'll add a disabled checked checkbox, and hide the original checkbox with CSS return `
  • ${innerHtml}
  • `; } else {// If the checkbox was not checked, we'll add a non-checked disabled checkbox, and hide the original checkbox with CSS. return `
  • ${innerHtml}
  • `; } }; // Creating a custom table renderer to add Bootstrap's responsive table styles to markdown tables. customRenderer.table = function(headerHtml, bodyHtml) { return `
    \n\n${headerHtml}\n\n${bodyHtml}\n\n
    \n
    `; }; markedOpts.renderer = customRenderer; // Now actually compile the markdown to HTML. marked(inputs.mdString, markedOpts, function afterwards (err, htmlString) { if (err) { return exits.error(err); } // If we're not allowing HTML, compile the input again with the `sanitize` option on. if (inputs.allowHtml === false) { markedOpts.sanitize = true; marked(inputs.mdString, markedOpts, function sanitized (err, sanitizedHtmlString) { if (err) { return exits.error(err); } // Now compare the unsanitized and the sanitized output, and if they're not the same, // leave through the `unsafeMarkdown` exit since it means that HTML tags were detected. if (htmlString !== sanitizedHtmlString) { return exits.unsafeMarkdown(); } return exits.success(htmlString); }); } else { return exits.success(htmlString); } }); } };