Skip to content

Commit 6e967f6

Browse files
ststimacmolant
authored andcommitted
Fix: Update visual of rules landing page
* Update how the rule index looks like * The rule index is now generated automatically * Rules in multi-rule packages are listed too * Each rule category has now a landing page - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Fix #391
1 parent 4368b8a commit 6e967f6

File tree

18 files changed

+502
-114
lines changed

18 files changed

+502
-114
lines changed

_config.yml

+19
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,22 @@ descriptions:
109109
"contributor-guide/development-environment": What you need to do to start developing for sonar
110110
"contributor-guide/index.md": Learn about the different pieces that build sonarwhal and what each one of them does
111111

112+
categories:
113+
accessibility:
114+
description: Is your site usable via the keyboard? Does your content have enough contrast to be read without issues? Everyone should enjoy your content. Let us help you!
115+
name: Accessibility
116+
development:
117+
description: "Web developers use a wide variety of tools: bundlers, transpilers, optimizers... But sometimes there can be problems when using them together or the configuration might not be the most optimal. Let us take care of this for you!"
118+
name: Development
119+
interoperability:
120+
description: The web has been around for decades and it can be accessed from many different devices and form factors, old and new. These rules help you make your sites more interoperable so your users can have a better experience no matter how they visit you.
121+
name: Interoperability
122+
pwa:
123+
description: Progressive Web Apps allow your users to have a more immersive experience within your site. Make sure your PWA looks the best no matter the platform of your users.
124+
name: PWA
125+
performance:
126+
description: No one likes to wait while a website loads. Validate that you are using the right compression, cache strategy and more to make your site (re)load faster.
127+
name: Performance
128+
security:
129+
description: Browsers are one of the main vectors for attacks. The web platform has many features that allow you to mitigate the risks. Are you doing everything you can to keep your users safe?
130+
name: Security

helpers/update-content.js

+158-12
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,177 @@
11
/* 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');
26
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
313

414
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`;
618
const TMP_DIR = require('./mktemp')();
719
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+
};
899

9100
config.fatal = true;
10101

11102
exec(`git clone ${CLONE_URL} "${TMP_DIR}"`);
12103

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', '')}`;
16132

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+
}
21137

22-
const rules = ls('-R', `${PACKAGES_TMP_DIR}/rule-*/README.md`);
138+
const destRuleDocPath = `${DEST_RULES_DIR}/${ruleName}.md`;
23139

140+
mv(ruleDocPath, destRuleDocPath);
141+
});
142+
143+
// Get category information of rules.
24144
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
27172

28-
mv(rulePath, destRulePath);
173+
processedCategories.categories.forEach((category) => {
174+
safeWriteFile(`${DEST_RULES_DIR}/${category.name}/index.md`, `# ${category.name}${os.EOL}`);
29175
});
30176

31177
rm('-rf', TMP_DIR);

helpers/updater.js

+6
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ const generateFrontMatterInfo = (filePath, title, description, currentFrontMatte
136136
tocTitle: tocTitle ? tocTitle.replace(/-/g, ' ') : tocTitle
137137
};
138138

139+
if (originalFile.endsWith('index.md')) {
140+
newFrontMatter.contentType = 'index';
141+
} else {
142+
newFrontMatter.contentType = 'details';
143+
}
144+
139145
const finalFrontMatterData = _.assign(newFrontMatter, existingFrontMatter);
140146
// Override frontmatter if there are existing frontmatter values.
141147

0 commit comments

Comments
 (0)