UNPKG

@salesforce-ux/eslint-plugin-slds

Version:

ESLint plugin provides custom linting rules specifically built for Salesforce Lightning Design System 2 (SLDS 2 beta)

331 lines (325 loc) 17.2 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // yaml-file:rule-messages.yml var require_rule_messages = __commonJS({ "yaml-file:rule-messages.yml"(exports2, module2) { module2.exports = { "no-slds-class-overrides": { "description": "Create new custom CSS classes instead of overriding SLDS selectors", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-class-overrides", "type": "problem", "messages": { "sldsClassOverride": "Overriding .{{className}} isn't supported. To differentiate SLDS and custom classes, create a CSS class in your namespace. Examples: myapp-input, myapp-button." } }, "no-deprecated-slds-classes": { "description": "Please replace the deprecated classes with a modern equivalent", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-slds-classes", "type": "problem", "messages": { "deprecatedClass": "The class {{className}} is deprecated and not available in SLDS2. Please update to a supported class." } }, "no-deprecated-tokens-slds1": { "description": "Update outdated design tokens to SLDS 2 styling hooks with similar values. For more information, see Styling Hooks on lightningdesignsystem.com.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-tokens-slds1", "type": "problem", "messages": { "deprecatedToken": "Consider removing {{oldValue}} or replacing it with {{newValue}}. Set the fallback to {{oldValue}}. For more info, see Styling Hooks on lightningdesignsystem.com.", "noReplacement": "Update outdated design tokens to SLDS 2 styling hooks with similar values. For more information, see Styling Hooks on lightningdesignsystem.com." } }, "enforce-sds-to-slds-hooks": { "description": "Convert your existing --sds styling hooks to --slds styling hooks. See lightningdesignsystem.com for more info.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-sds-to-slds-hooks", "type": "problem", "messages": { "replaceSdsWithSlds": "Replace {{oldValue}} with {{suggestedMatch}} styling hook." } }, "enforce-bem-usage": { "description": "Replace BEM double-dash syntax in class names with single underscore syntax", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-bem-usage", "type": "problem", "messages": { "bemDoubleDash": "{{actual}} has been retired. Update it to the new name {{newValue}}.", "fixBemNaming": "Update to correct BEM naming convention" } }, "modal-close-button-issue": { "description": "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#modal-close-button-issue", "type": "problem", "messages": { "modalCloseButtonIssue": "Update component attributes or CSS classes for the modal close button to comply with the modal component blueprint.", "removeClass": "Remove the slds-button_icon-inverse class from the modal close button in components that use the SLDS modal blueprint.", "changeVariant": "Change the variant attribute value from bare-inverse to bare in <lightning-button-icon> or <lightning-icon>.", "removeVariant": "Remove the variant attribute from the <lightning-icon> component inside the <button> element.", "ensureButtonClasses": "Add or move slds-button and slds-button_icon to the class attribute of the <button> element or <lightning-button-icon> component.", "ensureSizeAttribute": "To size icons properly, set the size attribute \u200Cto large in the <lightning-icon> and <lightning-button-icon> components." } }, "no-deprecated-classes-slds2": { "description": "Replace classes that aren't available with SLDS 2 classes", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-deprecated-classes-slds2", "type": "problem", "messages": { "deprecatedClass": "The class {{className}} isn't available in SLDS 2. Update it to a class supported in SLDS 2. See lightningdesignsystem.com for more information.", "updateToModernClass": "Replace deprecated class with modern equivalent", "checkDocumentation": "See lightningdesignsystem.com for SLDS 2 class alternatives" } }, "lwc-token-to-slds-hook": { "description": "Replace the deprecated --lwc tokens with the latest --slds tokens. See lightningdesignsystem.com for more info.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#lwc-token-to-slds-hook", "type": "problem", "messages": { "errorWithReplacement": "The '{{oldValue}}' design token is deprecated. Replace it with '{{newValue}}'. For more info, see Global Styling Hooks on lightningdesignsystem.com.", "errorWithStyleHooks": "The '{{oldValue}}' design token is deprecated. Replace it with the SLDS 2 '{{newValue}}' styling hook and set the fallback to '{{oldValue}}'. For more info, see Global Styling Hooks on lightningdesignsystem.com.", "errorWithNoRecommendation": "The '{{oldValue}}' design token is deprecated. For more info, see the New Global Styling Hook Guidance on lightningdesignsystem.com." } }, "no-sldshook-fallback-for-lwctoken": { "description": "Avoid using --slds styling hooks as fallback values for --lwc tokens.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-sldshook-fallback-for-lwctoken", "type": "problem", "messages": { "unsupportedFallback": "Remove the {{sldsToken}} styling hook that is used as a fallback value for {{lwcToken}}." } }, "no-unsupported-hooks-slds2": { "description": "Identifies styling hooks that aren't present in SLDS 2. They must be replaced with styling hooks that have a similar effect, or they must be removed.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-unsupported-hooks-slds2", "type": "problem", "messages": { "deprecated": "The {{token}} styling hook isn't present in SLDS 2 and there's no equivalent replacement. Remove it or replace it with a styling hook with a similar effect." } }, "no-slds-var-without-fallback": { "description": "Add fallback values to SLDS styling hooks. The fallback values are used in Salesforce environments where styling hooks are unavailable.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-var-without-fallback", "type": "problem", "messages": { "varWithoutFallback": "Your code uses the {{cssVar}} styling hook without a fallback value. Styling hooks are unavailable in some Salesforce environments. To render your component correctly in all environments, add this fallback value: var({{cssVar}}, {{recommendation}}). To make this fallback value brand-aware, use a branded design token instead of a static value. See Design Tokens on v1.lightningdesignsystem.com." } }, "no-slds-namespace-for-custom-hooks": { "description": "To differentiate custom styling hooks from SLDS styling hooks, create custom styling hooks in your namespace.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-namespace-for-custom-hooks", "type": "problem", "messages": { "customHookNamespace": "Using the --slds namespace for {{token}} isn't supported. Create the custom styling hook in your namespace. Example: --myapp-{{tokenWithoutNamespace}}" } }, "no-slds-private-var": { "description": "Some SLDS styling hooks are private and reserved only for internal Salesforce use. Private SLDS styling hooks have prefixes --_slds- and --slds-s-. For more information, look up private CSS in lightningdesignsystem.com.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-slds-private-var", "type": "problem", "messages": { "privateVar": "This styling hook is reserved for internal Salesforce use. Remove the --_slds- or \u2013slds-s private variable within selector {{prop}}. For more information, look up private CSS in lightningdesignsystem.com." } }, "enforce-component-hook-naming-convention": { "description": "Replace component styling hooks that use a deprecated naming convention.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#enforce-component-hook-naming-convention", "type": "problem", "messages": { "replace": "Replace the deprecated {{oldValue}} component styling hook with {{suggestedMatch}}." } }, "no-hardcoded-values-slds1": { "description": "Replace static values with SLDS 1 design tokens. For more information, look up design tokens on lightningdesignsystem.com.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-value", "type": "suggestion", "messages": { "hardcodedValue": "Replace the {{oldValue}} static value with an SLDS 1 styling hook: {{newValue}}.", "noReplacement": "There's no replacement styling hook for the {{oldValue}} static value. Remove the static value." } }, "no-hardcoded-values-slds2": { "description": "Replace static values with SLDS 2 styling hooks. For more information, look up design tokens on lightningdesignsystem.com.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#no-hardcoded-values-slds2", "type": "suggestion", "messages": { "hardcodedValue": "Consider replacing the {{oldValue}} static value with an SLDS 2 styling hook that has a similar value: {{newValue}}.", "noReplacement": "There's no replacement styling hook for the {{oldValue}} static value. Remove the static value." } }, "reduce-annotations": { "description": "Remove your annotations and update your code.", "url": "https://developer.salesforce.com/docs/platform/slds-linter/guide/reference-rules.html#reduce-annotations", "type": "problem", "messages": { "removeAnnotation": "Remove this annotation and update the code to SLDS best practices. For help, file an issue at https://github.com/salesforce-ux/slds-linter/" } } }; } }); // src/utils/node.ts var import_parser = require("@html-eslint/parser"); function findAttr(node, key) { return node.attributes.find( (attr) => attr.key && attr.key.value.toLowerCase() === key.toLowerCase() ); } function isAttributesEmpty(node) { return !node.attributes || node.attributes.length <= 0; } var lineBreakPattern = /\r\n|[\r\n\u2028\u2029]/u; var lineEndingPattern = new RegExp(lineBreakPattern.source, "gu"); // src/rules/enforce-bem-usage.ts var import_sds_metadata2 = __toESM(require("@salesforce-ux/sds-metadata")); var import_rule_messages2 = __toESM(require_rule_messages()); // src/rules/v9/enforce-bem-usage.ts var import_sds_metadata = __toESM(require("@salesforce-ux/sds-metadata")); var import_rule_messages = __toESM(require_rule_messages()); var { type, description, url, messages } = import_rule_messages.default["enforce-bem-usage"]; var bemMapping = import_sds_metadata.default.bemNaming; var enforce_bem_usage_default = { meta: { type, docs: { description, recommended: true, url }, fixable: "code", messages }, create(context) { return { // Check all class selectors for BEM usage "SelectorList Selector ClassSelector"(node) { const cssClassSelector = context.sourceCode.getText(node); const className = cssClassSelector.substring(1); if (className && className in bemMapping) { const newValue = bemMapping[className]; if (typeof newValue === "string") { context.report({ node, messageId: "bemDoubleDash", data: { actual: className, newValue }, fix(fixer) { return fixer.replaceText(node, `.${newValue}`); } }); } } } }; } }; // src/rules/enforce-bem-usage.ts var bemMapping2 = import_sds_metadata2.default.bemNaming; var deprecatedClasses = import_sds_metadata2.default.deprecatedClasses; var ruleConfig = import_rule_messages2.default["enforce-bem-usage"]; var { type: type2, description: description2, url: url2, messages: messages2 } = ruleConfig; var isDeprecatedClass = (className) => { return deprecatedClasses.includes(className) || deprecatedClasses.includes(bemMapping2[className]); }; var enforceBemUsageHtml = { create(context) { function check(node) { if (isAttributesEmpty(node)) { return; } const classAttr = findAttr(node, "class"); if (classAttr && classAttr.value) { const classNames = classAttr.value.value.split(/\s+/); classNames.forEach((className) => { if (className && className in bemMapping2 && !isDeprecatedClass(className)) { const classNameStart = classAttr.value.value.indexOf(className) + 7; const classNameEnd = classNameStart + className.length; const startLoc = { line: classAttr.loc.start.line, column: classAttr.loc.start.column + classNameStart }; const endLoc = { line: classAttr.loc.start.line, column: classAttr.loc.start.column + classNameEnd }; const newValue = bemMapping2[className]; context.report({ node, loc: { start: startLoc, end: endLoc }, messageId: "bemDoubleDash", data: { actual: className, newValue }, fix(fixer) { if (newValue) { const newClassValue = classAttr.value.value.replace( className, newValue ); return fixer.replaceTextRange( [classAttr.value.range[0], classAttr.value.range[1]], `${newClassValue}` ); } return null; } }); } }); } } return { Tag: check }; } }; var enforceBemUsage = { meta: { type: type2, docs: { recommended: true, description: description2, url: url2 }, fixable: "code", messages: messages2 }, create(context) { const filename = context.filename || context.getFilename(); if (filename.endsWith(".css") || filename.endsWith(".scss")) { try { return enforce_bem_usage_default.create(context); } catch (error) { return {}; } } else { return enforceBemUsageHtml.create(context); } } }; module.exports = enforceBemUsage; //# sourceMappingURL=enforce-bem-usage.js.map