|
1 | 1 | /* global config cp exec ls mv rm */
|
| 2 | +const _ = require('lodash'); |
| 3 | +const getDirname = require('path').dirname; |
| 4 | +const mkdirp = require('mkdirp'); |
| 5 | +const os = require('os'); |
2 | 6 | const shell = require('shelljs/global'); // eslint-disable-line no-unused-vars
|
| 7 | +const fs = require('fs'); |
| 8 | +const path = require('path'); |
| 9 | +const yamlLoader = require('js-yaml'); |
| 10 | + |
| 11 | +const configDir = path.resolve(__dirname, '..', '_config.yml'); |
| 12 | +const hexoConfig = yamlLoader.safeLoad(fs.readFileSync(configDir, 'utf8')); // eslint-disable-line no-sync |
3 | 13 |
|
4 | 14 | const CLONE_URL = 'https://github.com/sonarwhal/sonarwhal.git'; // eslint-disable-line no-process-env
|
5 |
| -const SOURCE_DIR = 'src/content'; |
| 15 | +const DEST_DIR = 'src/content'; |
| 16 | +const DEST_RULES_DIR = `${DEST_DIR}/docs/user-guide/rules`; |
| 17 | +const DATA_DIR = `${DEST_DIR}/_data`; |
6 | 18 | const TMP_DIR = require('./mktemp')();
|
7 | 19 | const PACKAGES_TMP_DIR = `${TMP_DIR}/packages`;
|
| 20 | +const categories = {}; |
| 21 | + |
| 22 | +const processRule = (rule, isSummary) => { |
| 23 | + const processedRule = { |
| 24 | + /* |
| 25 | + * For packages with multiple rules we have a summary |
| 26 | + * file with how to install and links to all the rules. |
| 27 | + * This property is used to not take into account in |
| 28 | + * the rule index page total. |
| 29 | + */ |
| 30 | + isSummary: isSummary === true, |
| 31 | + link: `/docs/user-guide/rules/${rule}/`, |
| 32 | + name: rule.replace(/^rule-/, '') |
| 33 | + }; |
| 34 | + |
| 35 | + return processedRule; |
| 36 | +}; |
| 37 | + |
| 38 | +/** |
| 39 | + * Process catogories to be suitable for Handlebars templating. |
| 40 | + * Before: { performance: ['rule-amp-validator'] } |
| 41 | + * After: |
| 42 | + * [{ |
| 43 | + * name: 'performance', |
| 44 | + * rules: [{ |
| 45 | + * link: 'rule-amp-validator/', |
| 46 | + * name: 'amp-validator' |
| 47 | + * }] |
| 48 | + * }] |
| 49 | + */ |
| 50 | +const processCategories = (cats) => { |
| 51 | + const processedCategories = _.reduce(cats, (allCategories, category) => { |
| 52 | + const includedRules = category.rules; |
| 53 | + |
| 54 | + const singleRules = includedRules.filter((ruleName) => { |
| 55 | + return !ruleName.includes('/'); |
| 56 | + }); |
| 57 | + |
| 58 | + /* |
| 59 | + * Rules with id: package-name/rule-name will be |
| 60 | + * considered part of a package with multiple rules |
| 61 | + */ |
| 62 | + const multiRules = includedRules.filter((ruleName) => { |
| 63 | + return ruleName.includes('/'); |
| 64 | + }); |
| 65 | + |
| 66 | + /* |
| 67 | + * Group rules by package name. |
| 68 | + */ |
| 69 | + const packagesName = _.groupBy(multiRules, (ruleName) => { |
| 70 | + return ruleName.split('/')[0]; |
| 71 | + }); |
| 72 | + |
| 73 | + const rules = _.map(singleRules, processRule); |
| 74 | + |
| 75 | + const multiRulesProcessed = _.reduce(packagesName, (multi, values, key) => { |
| 76 | + // Add an item with the link to the package with multiple rules itself. |
| 77 | + const partial = processRule(key, true); |
| 78 | + |
| 79 | + multi.push(partial); |
| 80 | + |
| 81 | + // Add an item for each individual rule for a package with multiple rules. |
| 82 | + return multi.concat(_.map(values, processRule)); |
| 83 | + }, []); |
| 84 | + |
| 85 | + const processedCategory = { |
| 86 | + description: category.description, |
| 87 | + link: `/docs/user-guide/rules/${category.name}/`, |
| 88 | + name: category.name, |
| 89 | + rules: rules.concat(multiRulesProcessed) |
| 90 | + }; |
| 91 | + |
| 92 | + allCategories.push(processedCategory); |
| 93 | + |
| 94 | + return allCategories; |
| 95 | + }, []); |
| 96 | + |
| 97 | + return { categories: processedCategories }; |
| 98 | +}; |
8 | 99 |
|
9 | 100 | config.fatal = true;
|
10 | 101 |
|
11 | 102 | exec(`git clone ${CLONE_URL} "${TMP_DIR}"`);
|
12 | 103 |
|
13 |
| -rm('-rf', `${SOURCE_DIR}/docs/contributor-guide`); |
14 |
| -rm('-rf', `${SOURCE_DIR}/docs/user-guide`); |
15 |
| -rm('-rf', `${SOURCE_DIR}/about`); |
| 104 | +rm('-rf', `${DEST_DIR}/docs/contributor-guide`); |
| 105 | +rm('-rf', `${DEST_DIR}/docs/user-guide`); |
| 106 | +rm('-rf', `${DEST_DIR}/about`); |
| 107 | + |
| 108 | +cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/contributor-guide`, `${DEST_DIR}/docs/contributor-guide`); |
| 109 | +cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/user-guide`, `${DEST_DIR}/docs/user-guide`); |
| 110 | +cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/about`, `${DEST_DIR}`); |
| 111 | +cp(`${PACKAGES_TMP_DIR}/sonarwhal/CHANGELOG.md`, `${DEST_DIR}/about`); |
| 112 | + |
| 113 | +const ruleDocs = ls('-R', `${PACKAGES_TMP_DIR}/rule-*/{README.md,/docs/*.md}`); |
| 114 | +const rules = ls('-R', `${PACKAGES_TMP_DIR}/rule-*/src/!(index).ts`); |
| 115 | + |
| 116 | +// Create folder if not exist before writing file. |
| 117 | +// Reference: https://stackoverflow.com/a/16317628 |
| 118 | +const safeWriteFile = (dir, content) => { |
| 119 | + const folderPath = getDirname(dir); |
| 120 | + |
| 121 | + mkdirp.sync(folderPath); |
| 122 | + fs.writeFileSync(dir, content); // eslint-disable-line no-sync |
| 123 | +}; |
| 124 | + |
| 125 | +// Get rule documentations. |
| 126 | +ruleDocs.forEach((ruleDocPath) => { |
| 127 | + const ruleDocPathSplitted = ruleDocPath.split('/').reverse(); |
| 128 | + let ruleName; |
| 129 | + |
| 130 | + if (ruleDocPathSplitted[1] === 'docs') { |
| 131 | + ruleName = `${ruleDocPathSplitted[2]}/${ruleDocPathSplitted[0].replace('.md', '')}`; |
16 | 132 |
|
17 |
| -cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/contributor-guide`, `${SOURCE_DIR}/docs/contributor-guide`); |
18 |
| -cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/user-guide`, `${SOURCE_DIR}/docs/user-guide`); |
19 |
| -cp('-R', `${PACKAGES_TMP_DIR}/sonarwhal/docs/about`, `${SOURCE_DIR}`); |
20 |
| -cp(`${PACKAGES_TMP_DIR}/sonarwhal/CHANGELOG.md`, `${SOURCE_DIR}/about`); |
| 133 | + mkdirp.sync(path.join(process.cwd(), DEST_RULES_DIR, path.dirname(ruleName))); |
| 134 | + } else { |
| 135 | + ruleName = ruleDocPathSplitted[1]; |
| 136 | + } |
21 | 137 |
|
22 |
| -const rules = ls('-R', `${PACKAGES_TMP_DIR}/rule-*/README.md`); |
| 138 | + const destRuleDocPath = `${DEST_RULES_DIR}/${ruleName}.md`; |
23 | 139 |
|
| 140 | + mv(ruleDocPath, destRuleDocPath); |
| 141 | +}); |
| 142 | + |
| 143 | +// Get category information of rules. |
24 | 144 | rules.forEach((rulePath) => {
|
25 |
| - const ruleName = rulePath.split('/').reverse()[1]; |
26 |
| - const destRulePath = `${SOURCE_DIR}/docs/user-guide/rules/${ruleName}.md`; |
| 145 | + const ruleContent = fs.readFileSync(rulePath, 'utf8'); // eslint-disable-line no-sync |
| 146 | + const ruleNameRegex = /id:\s*'([^']*)'/; |
| 147 | + const ruleNameMatch = ruleContent.match(ruleNameRegex); |
| 148 | + const categoryRegex = /category:\s*Category\.([a-z]*)/; |
| 149 | + const categoryMatch = ruleContent.match(categoryRegex); |
| 150 | + |
| 151 | + // If we don't find the category or the name, we will assume that it is not a rule. |
| 152 | + if (categoryMatch && ruleNameMatch) { |
| 153 | + const category = categoryMatch.pop(); |
| 154 | + const ruleName = `rule-${ruleNameMatch.pop()}`; |
| 155 | + |
| 156 | + if (categories[category]) { |
| 157 | + categories[category].rules.push(ruleName); |
| 158 | + } else { |
| 159 | + categories[category] = { |
| 160 | + description: hexoConfig.categories[category].description, |
| 161 | + name: hexoConfig.categories[category].name, |
| 162 | + rules: [ruleName] |
| 163 | + }; |
| 164 | + } |
| 165 | + } |
| 166 | +}); |
| 167 | + |
| 168 | +// Generate JSON file that contains the category-rule information. |
| 169 | +const processedCategories = processCategories(categories); |
| 170 | + |
| 171 | +fs.writeFileSync(`${DATA_DIR}/categories.json`, JSON.stringify(processedCategories), 'utf8'); //eslint-disable-line no-sync |
27 | 172 |
|
28 |
| - mv(rulePath, destRulePath); |
| 173 | +processedCategories.categories.forEach((category) => { |
| 174 | + safeWriteFile(`${DEST_RULES_DIR}/${category.name}/index.md`, `# ${category.name}${os.EOL}`); |
29 | 175 | });
|
30 | 176 |
|
31 | 177 | rm('-rf', TMP_DIR);
|
0 commit comments