/* This is the web builder script that generates the README file. Run using `npm run webber`. */ // Load modules const fs = require('fs-extra'), path = require('path'), chalk = require('chalk'), md = require('markdown-it')(), minify = require('html-minifier').minify; const util = require('./util'); var Prism = require('prismjs'); const unescapeHTML = str => str.replace( /&|<|>|'|"/g, tag => ({ '&': '&', '<': '<', '>': '>', ''': "'", '"': '"' }[tag] || tag) ); if(util.isTravisCI() && /^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])) { console.log(`${chalk.green('NOBUILD')} index build terminated, parent commit is a Travis build!`); process.exit(0); } // Compile the mini.css framework and custom CSS styles, using `node-sass`. const sass = require('node-sass'); sass.render( { file: path.join('docs', 'mini', 'flavor.scss'), outFile: path.join('docs', 'mini.css'), outputStyle: 'compressed' }, function(err, result) { if (!err) { fs.writeFile(path.join('docs', 'mini.css'), result.css, function(err2) { if (!err2) console.log(`${chalk.green('SUCCESS!')} mini.css file generated!`); else console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`); }); } else { console.log(`${chalk.red('ERROR!')} During mini.css file generation: ${err}`); } } ); // Set variables for paths const snippetsPath = './snippets', staticPartsPath = './static-parts', docsPath = './docs'; // Set variables for script let snippets = {}, beginnerSnippetNames = ['everyNth', 'filterNonUnique', 'last.md', 'maxN', 'minN', 'nthElement', 'sample', 'similarity', 'tail', 'currentURL', 'hasClass', 'getMeridiemSuffixOfInteger', 'factorial', 'fibonacci', 'gcd', 'isDivisible', 'isEven', 'isPrime', 'lcm', 'randomIntegerInRange', 'sum', 'reverseString', 'truncateString'], startPart = '', endPart = '', output = '', startBeginnerPart = '', endBegginerPart = '', beginnerOutput = '', pagesOutput = []; tagDbData = {}; // Start the timer of the script console.time('Webber'); // Synchronously read all snippets and sort them as necessary (case-insensitive) snippets = util.readSnippets(snippetsPath); // Load static parts for the index.html file try { startPart = fs.readFileSync(path.join(staticPartsPath, 'page-start.html'), 'utf8'); endPart = fs.readFileSync(path.join(staticPartsPath, 'page-end.html'), 'utf8'); startBeginnerPart = fs.readFileSync(path.join(staticPartsPath, 'beginner-page-start.html'), 'utf8'); endBeginnerPart = fs.readFileSync(path.join(staticPartsPath, 'beginner-page-end.html'), 'utf8'); } catch (err) { // Handle errors (hopefully not!) console.log(`${chalk.red('ERROR!')} During static part loading: ${err}`); process.exit(1); } // Load tag data from the database tagDbData = util.readTags(); // Create the output for the index.html file try { // Add the start static part output += `${startPart + '\n'}`; // Loop over tags and snippets to create the table of contents for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1][0]))] .filter(v => v) .sort((a, b) => util.capitalize(a, true) === 'Uncategorized' ? 1 : util.capitalize(b, true) === 'Uncategorized' ? -1 : a.localeCompare(b))) { output += `

` + md .render(`${util.capitalize(tag, true)}\n`) .replace(/

/g, '') .replace(/<\/p>/g, '') + `

`; for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) output += md .render(`[${taggedSnippet[0]}](./${tag}.html#${taggedSnippet[0].toLowerCase()})\n`) .replace(/

/g, '') .replace(/<\/p>/g, '') .replace(/

`; output += ` `; // Loop over tags and snippets to create the list of snippets for (let tag of [...new Set(Object.entries(tagDbData).map(t => t[1][0]))] .filter(v => v) .sort((a, b) => util.capitalize(a, true) === 'Uncategorized' ? 1 : util.capitalize(b, true) === 'Uncategorized' ? -1 : a.localeCompare(b))) { let localOutput = output.replace(/\$tag/g, util.capitalize(tag)).replace(new RegExp(`./${tag}.html#`,'g'),'#'); localOutput += md .render(`## ${util.capitalize(tag, true)}\n`) .replace(/

/g, '

'); for (let taggedSnippet of Object.entries(tagDbData).filter(v => v[1][0] === tag)) localOutput += '
' + md .render(`\n${snippets[taggedSnippet[0] + '.md']}`) .replace(/

/g, `${taggedSnippet[1].includes('advanced')?'advanced':''}

`) .replace(/<\/h3>/g, '

') .replace(/
([^\0]*?)<\/code><\/pre>/gm, (match, p1) => `
${Prism.highlight(unescapeHTML(p1), Prism.languages.javascript)}
`) .replace(/<\/pre>\s+
📋 Copy to clipboard' +
          '
'; // Add the ending static part localOutput += `\n${endPart + '\n'}`; localOutput = util.optimizeNodes(localOutput, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); // Optimize operator nodes localOutput = util.optimizeNodes(localOutput, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); // Optimize keyword nodes localOutput = util.optimizeNodes(localOutput, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); pagesOutput.push({'tag': tag,'content': localOutput}); } // // Optimize punctuation nodes // output = util.optimizeNodes(output, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); // // Optimize operator nodes // output = util.optimizeNodes(output, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); // // Optimize keyword nodes // output = util.optimizeNodes(output, /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm, (match, p1, p2, p3) => `${p1}${p2}${p3}`); // Minify output pagesOutput.forEach(page => { page.content = minify(page.content, { collapseBooleanAttributes: true, collapseWhitespace: true, decodeEntities: false, minifyCSS: true, minifyJS: true, keepClosingSlash: true, processConditionalComments: true, removeAttributeQuotes: false, removeComments: true, removeEmptyAttributes: false, removeOptionalTags: false, removeScriptTypeAttributes: false, removeStyleLinkTypeAttributes: false, trimCustomFragments: true }); fs.writeFileSync(path.join(docsPath, page.tag+'.html'), page.content); }) // Write to the index.html file //fs.writeFileSync(path.join(docsPath, 'index.html'), output); } catch (err) { // Handle errors (hopefully not!) console.log(`${chalk.red('ERROR!')} During index.html generation: ${err}`); process.exit(1); } // Create the output for the beginner.html file try { // Add the static part beginnerOutput += `${startBeginnerPart + '\n'}`; // Filter begginer snippets const filteredBeginnerSnippets = Object.keys(snippets) .filter(key => beginnerSnippetNames.map(name => name+'.md').includes(key)) .reduce((obj, key) => { obj[key] = snippets[key]; return obj; }, {}); console.log(filteredBeginnerSnippets); for (let snippet of Object.entries(filteredBeginnerSnippets)) beginnerOutput += '
' + md .render(`\n${snippets[snippet[0]]}`) .replace(/

/g, `${snippet[1].includes('advanced')?'advanced':''}

`) .replace(/<\/h3>/g, '
') .replace(/
([^\0]*?)<\/code><\/pre>/gm, (match, p1) => `
${Prism.highlight(unescapeHTML(p1), Prism.languages.javascript)}
`) .replace(/<\/pre>\s+
📋 Copy to clipboard' +
          '
'; beginnerOutput += '
' + // begginer snippet goes here. '' + '
'; beginnerOutput += `${endBeginnerPart}`; // Generate 'beginner.html' file fs.writeFileSync(path.join(docsPath, 'beginner.html'), beginnerOutput); } catch (err) { console.log(`${chalk.red('ERROR!')} During beginner.html generation: ${err}`); process.exit(1); } // Log a success message console.log(`${chalk.green('SUCCESS!')} index.html file generated!`); // Log the time taken console.timeEnd('Webber');