ember-template-lint
Version:
Linter for Ember or Handlebars templates.
112 lines (94 loc) • 3.04 kB
JavaScript
const createErrorMessage = require('../helpers/create-error-message');
const NodeMatcher = require('../helpers/node-matcher');
const Rule = require('./base');
function ERROR_MESSAGE_FORBIDDEN_ELEMENTS(element) {
return `Use of <${element}> detected. Do not use forbidden elements.`;
}
const FORBIDDEN_ELEMENTS = ['meta', 'style', 'html', 'script'];
const DEFAULT_CONFIG = {
forbidden: FORBIDDEN_ELEMENTS,
};
function isValidConfigObjectFormat(config) {
for (let key in config) {
let value = config[key];
let valueIsArray = Array.isArray(value);
if (key === 'forbidden' && !valueIsArray) {
return false;
} else if (!DEFAULT_CONFIG[key]) {
return false;
}
}
return true;
}
function sanitizeConfigArray(forbidden = []) {
return forbidden.filter(Boolean).sort((a, b) => b.length - a.length);
}
function hasHeadParent(path) {
let parents = [...path.parents()];
let refParentNode = {
type: 'ElementNode',
tag: 'head',
};
let hasHeadElementInParentPath = parents.some((parent) =>
NodeMatcher.match(parent.node, refParentNode)
);
return Boolean(hasHeadElementInParentPath);
}
module.exports = class NoForbiddenElements extends Rule {
parseConfig(config) {
let configType = typeof config;
switch (configType) {
case 'boolean':
// if `true` use `DEFAULT_CONFIG`
return config ? DEFAULT_CONFIG : false;
case 'object':
if (Array.isArray(config)) {
return {
forbidden: sanitizeConfigArray(config),
};
} else if (isValidConfigObjectFormat(config)) {
// default any missing keys to empty values
return {
forbidden: sanitizeConfigArray(config.forbidden || FORBIDDEN_ELEMENTS),
};
}
break;
case 'undefined':
return false;
}
let errorMessage = createErrorMessage(
this.ruleName,
[
' * boolean - `true` to enable / `false` to disable',
' * array -- an array of strings to forbid, default: ["meta", "style", "html", "style"]',
' * object -- An object with the following keys:',
' * `forbidden` -- An array of forbidden strings',
],
config
);
throw new Error(errorMessage);
}
isInHeadHbsFile(scope) {
return scope._filePath === 'app/templates/head.hbs';
}
visitor() {
return {
ElementNode(node, path) {
if (node.tag === 'meta' && (hasHeadParent(path) || this.isInHeadHbsFile(this))) {
return;
}
let isForbiddenElement = this.config.forbidden.includes(node.tag);
if (isForbiddenElement) {
this.log({
message: ERROR_MESSAGE_FORBIDDEN_ELEMENTS(node.tag),
line: node.loc && node.loc.start.line,
column: node.loc && node.loc.start.column,
source: this.sourceForNode(node),
});
}
},
};
}
};
module.exports.ERROR_MESSAGE_FORBIDDEN_ELEMENTS = ERROR_MESSAGE_FORBIDDEN_ELEMENTS;
;