const fs = require('fs'); const path = require('path'); const program = require('commander'); const lodash = require('lodash'); const jsyaml = require('js-yaml'); const remark = require('remark'); const frontMatter = require('remark-frontmatter'); const mdCompact = require('mdast-util-compact'); const minimatch = require('minimatch'); const si = require('./sourceInfoClasses'); // "Aggregate" data collected over the whole file set. const aggData = {}; const toolsFolderName = 'tools'; const configFileName = 'doctool.config.json'; const defaultFolder = path.resolve('docs'); const sourceInfoFolder = path.resolve('docs', 'sourceinfo'); function filterFiles(filePath) { let isAllowed = true; this.excludedFileList = aggData['config'].exclude; if (this.excludedFileList) { isAllowed = this.excludedFileList.filter((pattern) => { return minimatch(filePath, pattern.toString(), { nocase: true }); }).length === 0; } return isAllowed; } const toolModules = loadToolModules(); let toolList; function updatePhase(mdCache, aggData) { toolList.forEach((toolName) => { console.log(`Tool: ${toolName}`); toolModules[toolName].processDocs(mdCache, aggData); }); const filenames = Object.keys(mdCache); for (let i = 0; i < filenames.length; i++) { const pathname = filenames[i]; const tree = mdCache[pathname].mdOutTree; const original = mdCache[pathname].mdInTree; if (program.json) { let filename = path.basename(pathname); console.log(`\nFile "${filename}" before processing:`); console.log(JSON.stringify(original)); console.log(`\nFile "${filename}" after processing:`); console.log(JSON.stringify(tree)); } if (!lodash.isEqual(tree, original)) { if (program.verbose) { console.log(`Modified: ${pathname}`); } fs.writeFileSync( filenames[i], remark() .use(frontMatter, { type: 'yaml', fence: '---' }) .data('settings', { paddedTable: false, gfm: false }) .stringify(tree) ); } } } function minimiseTree(tree) { let minPropsTree = JSON.parse(JSON.stringify(tree, (key, value) => (key === 'position' ? undefined : value))); mdCompact(minPropsTree); return minPropsTree; } function loadToolModules() { const mods = {}; const toolsFolderPath = path.resolve(__dirname, toolsFolderName); const modFiles = fs.readdirSync(toolsFolderPath); for (let i = 0; i < modFiles.length; i++) { const modPath = path.resolve(toolsFolderPath, modFiles[i]); if (path.extname(modPath) === '.js') { const toolName = path.basename(modPath, '.js'); mods[toolName] = require(modPath); } } return mods; } function loadConfig() { const configFilePath = path.resolve(__dirname, configFileName); return JSON.parse(fs.readFileSync(configFilePath)); } function getAllDocFilePaths(docFolder, files) { const items = fs.readdirSync(docFolder); for (let i = 0; i < items.length; i++) { const itemPath = path.resolve(docFolder, items[i]); const itemInfo = fs.statSync(itemPath); if (itemInfo.isFile()) { files.push(itemPath); } else if (itemInfo.isDirectory()) { getAllDocFilePaths(itemPath, files); } } } function initMdCache(filenames) { const mdCache = {}; for (let i = 0; i < filenames.length; i++) { const pathname = filenames[i]; mdCache[pathname] = {}; const src = fs.readFileSync(pathname); const tree = remark().use(frontMatter, ['yaml']).parse(src); mdCache[pathname].mdInTree = minimiseTree(tree); mdCache[pathname].mdOutTree = minimiseTree(tree); } return mdCache; } function initClassInfo(aggData) { const yamlFilenames = fs.readdirSync(path.resolve(sourceInfoFolder)); aggData.classInfo = {}; yamlFilenames.forEach((yamlFilename) => { const classYamlText = fs.readFileSync(path.resolve(sourceInfoFolder, yamlFilename), 'utf8'); const classYaml = jsyaml.safeLoad(classYamlText); if (program.verbose) { console.log(classYaml.items[0].name); } aggData.classInfo[classYaml.items[0].name] = new si.ComponentInfo(classYaml); }); } program .usage('[options] ') .option('-p, --profile [profileName]', 'Select named config profile', 'default') .option('-j, --json', 'Output JSON data for Markdown syntax tree') .option('-v, --verbose', 'Log doc files as they are processed') .option('-t, --timing', 'Output time taken for run') .parse(process.argv); let startTime; if (program.timing) { startTime = process.hrtime(); } let sourcePath; if (program.args.length === 0) { sourcePath = defaultFolder; } else { sourcePath = path.resolve(program.args[0]); } const sourceInfo = fs.statSync(sourcePath); const config = loadConfig(); aggData['config'] = config; if (config.profiles[program.profile]) { toolList = config.profiles[program.profile]; var toolListText = toolList.join(', '); console.log(`Using '${program.profile}' profile: ${toolListText}`); } else { console.log(`Aborting: unknown profile '${program.profile}`); return 0; } let files = []; if (sourceInfo.isDirectory()) { getAllDocFilePaths(sourcePath, files); aggData['rootFolder'] = path.dirname(sourcePath); } else if (sourceInfo.isFile()) { files = [sourcePath]; } files = files.filter((filename) => filename !== undefined && path.extname(filename) === '.md' && filename !== 'README.md' && filterFiles(filename)); const mdCache = initMdCache(files); console.log('Loading source data...'); initClassInfo(aggData); console.log('Updating Markdown files...'); updatePhase(mdCache, aggData); if (program.timing) { const endTime = process.hrtime(startTime); console.log(`Run complete in ${endTime[0]} sec`); }