167 lines
5.1 KiB
JavaScript
167 lines
5.1 KiB
JavaScript
const _ = require('lodash');
|
|
const spec = require('../specs');
|
|
const {versions} = require('../utils');
|
|
const ASTLinter = require('../ast-linter');
|
|
|
|
function getRules(id, options) {
|
|
const checkVersion = _.get(options, 'checkVersion', versions.default);
|
|
let ruleSet = spec.get([checkVersion]);
|
|
|
|
const ruleRegex = new RegExp('^' + id + '-.*', 'g');
|
|
ruleSet = _.pickBy(ruleSet.rules, function (rule, ruleCode) {
|
|
if (ruleCode.match(ruleRegex)) {
|
|
return rule;
|
|
}
|
|
});
|
|
|
|
return ruleSet;
|
|
}
|
|
|
|
function getLogger({theme, rule, file = null}) {
|
|
return {
|
|
failure: (content) => {
|
|
if (!theme.results.fail[rule.code]) {
|
|
theme.results.fail[rule.code] = {failures: []};
|
|
}
|
|
const failure = {
|
|
...content,
|
|
rule: rule.code
|
|
};
|
|
if (file) {
|
|
failure.ref = file.file;
|
|
}
|
|
theme.results.fail[rule.code].failures.push(failure);
|
|
}
|
|
};
|
|
}
|
|
|
|
function applyRule(rule, theme) {
|
|
// The result variable is passed around to keep a state through the full rull lifecycle
|
|
const result = {};
|
|
try {
|
|
// Check if the rule is enabled (optional)
|
|
if (typeof rule.isEnabled === 'function') {
|
|
if (!rule.isEnabled({theme, log: getLogger({theme, rule}), result})) {
|
|
return;
|
|
}
|
|
} else if (typeof rule.isEnabled === 'boolean' && !rule.isEnabled) {
|
|
return;
|
|
}
|
|
|
|
// Initialize the rule (optional)
|
|
if (typeof rule.init === 'function') {
|
|
rule.init({theme, log: getLogger({theme, rule}), result});
|
|
}
|
|
|
|
// Run the main function on each theme file (optional)
|
|
if (typeof rule.eachFile === 'function') {
|
|
_.each(theme.files , function (themeFile) {
|
|
rule.eachFile({file: themeFile, theme, log: getLogger({theme, rule}), result});
|
|
});
|
|
}
|
|
|
|
// Run the final function
|
|
if (typeof rule.done === 'function') {
|
|
rule.done({theme, log: getLogger({theme, rule}), result});
|
|
}
|
|
} catch (e) {
|
|
// Output something instead of failing silently (should never happen)
|
|
// eslint-disable-next-line
|
|
console.error('gscan failure', e);
|
|
}
|
|
}
|
|
|
|
function parseWithAST({theme, log, file, rules, callback}){
|
|
const linter = new ASTLinter();
|
|
|
|
// This rule is needed to find partials
|
|
// Partials are needed for a full parsing
|
|
if (!rules['mark-used-partials']) {
|
|
rules['mark-used-partials'] = require(`../ast-linter/rules/mark-used-partials`);
|
|
}
|
|
|
|
function processFile(themeFile) {
|
|
if (themeFile.parsed.error) {
|
|
// Ignore parsing errors, they are handled in 005
|
|
return;
|
|
}
|
|
|
|
const astResults = linter.verify({
|
|
parsed: themeFile.parsed,
|
|
rules,
|
|
source: themeFile.content,
|
|
moduleId: themeFile.file
|
|
});
|
|
|
|
if (astResults.length) {
|
|
log.failure({
|
|
message: astResults[0].message
|
|
});
|
|
}
|
|
|
|
if (typeof callback === 'function') {
|
|
callback(linter);
|
|
}
|
|
|
|
linter.partials.forEach(({normalizedName}) => {
|
|
const partialFile = theme.files.find(f => f.normalizedFile === `partials/${normalizedName}.hbs`);
|
|
if (partialFile) {
|
|
processFile(partialFile);
|
|
}
|
|
});
|
|
}
|
|
|
|
return processFile(file);
|
|
}
|
|
|
|
const ruleImplementations = {
|
|
'GS100-NO-UNUSED-CUSTOM-THEME-SETTING': {
|
|
isEnabled: ({theme}) => {
|
|
return !!theme.customSettings;
|
|
},
|
|
init: ({result}) => {
|
|
result.customThemeSettings = new Set();
|
|
},
|
|
eachFile: ({file, theme, log, result}) => {
|
|
let templateTest = file.file.match(/(?<!partials\/.+?)\.hbs$/);
|
|
|
|
if (templateTest) {
|
|
parseWithAST({file, theme, rules: {
|
|
'mark-used-custom-theme-setting': require(`../ast-linter/rules/mark-used-custom-theme-settings`)
|
|
}, log, callback: (linter) => {
|
|
linter.customThemeSettings.forEach((variable) => {
|
|
result.customThemeSettings.add(variable);
|
|
});
|
|
}});
|
|
}
|
|
},
|
|
done: ({log, theme, result}) => {
|
|
const config = Object.keys(theme.customSettings);
|
|
const notUsedVariable = config.filter(x => !result.customThemeSettings.has(x));
|
|
|
|
if (notUsedVariable.length > 0) {
|
|
log.failure({
|
|
message: `Found unused variables: ${notUsedVariable.map(x => '@custom.' + x).join(', ')}`,
|
|
ref: 'package.json'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function checkUsage(theme, options) {
|
|
const rules = getRules('GS100', options);
|
|
|
|
_.each(rules, function (check, ruleCode) {
|
|
applyRule({
|
|
code: ruleCode,
|
|
...check,
|
|
...ruleImplementations[ruleCode]
|
|
}, theme);
|
|
});
|
|
|
|
return theme;
|
|
}
|
|
|
|
module.exports = checkUsage;
|