eslint-plugin-vue-scoped-css
Version:
ESLint plugin for Scoped CSS in Vue.js
185 lines (184 loc) • 7.87 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const lodash_1 = __importDefault(require("lodash"));
const context_1 = require("../styles/context");
const compat_1 = require("../utils/compat");
const styleTypesAttrs = ["scoped", "module"];
module.exports = {
meta: {
docs: {
description: "enforce the `<style>` tags to be plain or have the `scoped` or `module` attribute",
categories: ["vue2-recommended", "vue3-recommended"],
default: "warn",
url: "https://future-architect.github.io/eslint-plugin-vue-scoped-css/rules/enforce-style-type.html",
suggestion: true,
},
fixable: null,
messages: {
add: "Add attribute `{{ attribute }}`.",
remove: "Remove attribute `{{ attribute }}`.",
removeMultiple: "Remove attributes {{ attributes }}.",
change: "Change `{{ fromAttribute }}` to `{{ toAttribute }}` attribute.",
forbiddenStyle: "`{{ attribute }}` attribute is forbidden.",
forbiddenPlain: "Missing attribute {{ attributes }}.",
forbiddenScopedModule: "Cannot use both `scoped` and `module` attributes.",
},
schema: [
{
type: "object",
properties: {
allows: {
type: "array",
minItems: 1,
uniqueItems: true,
items: {
type: "string",
enum: ["plain", "scoped", "module"],
},
},
},
additionalProperties: false,
},
],
type: "suggestion",
hasSuggestions: true,
},
create(context) {
var _a, _b, _c, _d;
const styles = (0, context_1.getStyleContexts)(context).filter(context_1.isValidStyleContext);
if (!styles.length) {
return {};
}
const reporter = (0, context_1.getCommentDirectivesReporter)(context);
const sourceCode = (0, compat_1.getSourceCode)(context);
const tokenStore = (_b = (_a = sourceCode.parserServices).getTemplateBodyTokenStore) === null || _b === void 0 ? void 0 : _b.call(_a);
const { options } = context;
const allows = (_d = (_c = options[0]) === null || _c === void 0 ? void 0 : _c.allows) !== null && _d !== void 0 ? _d : ["scoped"];
const singleAllow = allows.length === 1 && allows[0];
function removeAttr(fixer, node) {
const { attributes } = node.parent;
const prevToken = tokenStore.getTokenBefore(node);
const nextToken = tokenStore.getTokenAfter(node);
return [
fixer.removeRange([
prevToken.range[1],
attributes.length === 1 ? nextToken.range[0] : node.range[1],
]),
];
}
function reportForbiddenStyle(node, attribute) {
const forbiddenAttr = node.startTag.attributes.find((attr) => attr.key.name === attribute);
const forbiddenAttrName = forbiddenAttr.key.name;
reporter.report({
node: forbiddenAttr,
messageId: "forbiddenStyle",
data: {
attribute,
},
suggest: [
singleAllow && singleAllow !== "plain"
? {
messageId: "change",
data: {
fromAttribute: forbiddenAttrName,
toAttribute: singleAllow,
},
fix(fixer) {
return fixer.replaceText(forbiddenAttr, singleAllow);
},
}
: {
messageId: "remove",
data: {
attribute: forbiddenAttrName,
},
fix(fixer) {
return removeAttr(fixer, forbiddenAttr);
},
},
],
});
}
function reportForbiddenPlain(node) {
reporter.report({
node: node.startTag,
messageId: "forbiddenPlain",
data: {
attributes: allows.map((allow) => `\`${allow}\``).join(" or "),
},
suggest: singleAllow
? [
{
messageId: "add",
data: {
attribute: singleAllow,
},
fix(fixer) {
const close = tokenStore.getLastToken(node.startTag);
return (close && fixer.insertTextBefore(close, ` ${singleAllow}`));
},
},
]
: undefined,
});
}
function reportForbiddenScopedModule(node) {
const forbiddenAttrs = node.startTag.attributes.filter((attr) => styleTypesAttrs.includes(attr.key.name) && !allows.includes(attr.key.name));
reporter.report({
node: node.startTag,
messageId: "forbiddenScopedModule",
suggest: forbiddenAttrs.length
? [
forbiddenAttrs.length === 1
? {
messageId: "remove",
data: {
attribute: forbiddenAttrs[0].key.name.toString(),
},
fix(fixer) {
return removeAttr(fixer, forbiddenAttrs[0]);
},
}
: {
messageId: "removeMultiple",
data: {
attributes: forbiddenAttrs
.map((attr) => `\`${attr.key.name}\``)
.join(", "),
},
fix(fixer) {
return lodash_1.default.flatMap(forbiddenAttrs, (attr) => removeAttr(fixer, attr));
},
},
]
: undefined,
});
}
return {
"Program:exit"() {
for (const style of styles) {
if (style.scoped && style.module) {
reportForbiddenScopedModule(style.styleElement);
}
else if (style.scoped) {
if (!allows.includes("scoped")) {
reportForbiddenStyle(style.styleElement, "scoped");
}
}
else if (style.module) {
if (!allows.includes("module")) {
reportForbiddenStyle(style.styleElement, "module");
}
}
else {
if (!allows.includes("plain")) {
reportForbiddenPlain(style.styleElement);
}
}
}
},
};
},
};
;