@manifoldco/eslint-plugin-stencil
Version:
ESLint rules to enforce better practices in Stencil development
142 lines (135 loc) • 3.93 kB
JavaScript
module.exports.rules = {
"require-render-decorator": {
meta: {
messages: {
decoratorMissing:
"Each component's render method must be decorated with the @{{ decoratorName }} decorator."
},
schema: [
{
type: "object",
properties: {
decoratorName: {
type: "string"
}
}
}
]
},
create: context => ({
MethodDefinition(node) {
const ancestors = context.getAncestors();
const isComponent = ancestors.some(a => {
const isClass =
a.type === "ExportNamedDeclaration" &&
a.declaration.type === "ClassDeclaration";
if (isClass) {
const decorators = a.declaration.decorators || [];
return decorators.some(
d => d.expression.callee.name === "Component"
);
}
return false;
});
if (isComponent && node.key.name === "render") {
const decorators = node.decorators || [];
const option = context.options[0] || {
decoratorName: "logger"
};
const logger = decorators.find(
d => d.expression.callee.name === option.decoratorName
);
if (!logger) {
context.report({
node,
messageId: "decoratorMissing",
data: {
decoratorName: option.decoratorName
}
});
}
}
}
})
},
"restrict-required-props": {
meta: {
messages: {
attributesOnly:
"The property {{ name }} must either be optional or have a default value assigned. Only properties that can be set from HTML attributes should be required unless default values are provide (only booleans and strings can be set via attributes)."
},
schema: []
},
create: context => ({
ClassProperty(node) {
const decorators = node.decorators || [];
const prop = decorators.find(d => d.expression.callee.name === "Prop");
if (prop) {
const allowedTypes = ["TSBooleanKeyword", "TSStringKeyword"];
if (
!node.optional &&
!node.value &&
!allowedTypes.includes(node.typeAnnotation.typeAnnotation.type)
) {
context.report({
node,
messageId: "attributesOnly",
data: {
name: node.key.name
}
});
}
}
}
})
},
"component-prefix": {
meta: {
messages: {
badPrefix: "Component {{ name }} should be prefixed with '{{ prefix }}'"
},
schema: [
{
type: "object",
properties: {
prefix: {
type: "string"
}
}
}
]
},
create: context => ({
ExportNamedDeclaration(node) {
if (node.declaration.type === "ClassDeclaration") {
const decorators = node.declaration.decorators || [];
const component = decorators.find(
d => d.expression.callee.name === "Component"
);
if (component) {
const tag = component.expression.arguments[0].properties.find(
p => p.key.name === "tag"
);
if (tag) {
const name = tag.value.value;
const prefixOption = context.options[0] || {
prefix: "manifold-"
};
const prefix = prefixOption.prefix;
if (!name.startsWith(prefix)) {
context.report({
node,
messageId: "badPrefix",
data: {
name,
prefix
}
});
}
}
}
}
}
})
}
};