UNPKG

@atlaskit/eslint-plugin-design-system

Version:

The essential plugin for use with the Atlassian Design System.

279 lines (276 loc) 11.9 kB
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } import { createLintRule } from '../utils/create-lint-rule'; var EASING_KEYWORDS = ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear', 'step-start', 'step-end']; var KEYWORD_VALUES = ['none', 'all', 'inherit', 'initial', 'unset']; var isDuration = function isDuration(token) { return /^(?:0|\d+(?:\.\d+)?m?s)$/.test(token); }; var isEasing = function isEasing(token) { return EASING_KEYWORDS.includes(token) || token.startsWith('cubic-bezier(') || token.startsWith('steps('); }; /** * Tokenizes a CSS shorthand value string, respecting function boundaries. * e.g. 'opacity 200ms cubic-bezier(0.4, 0, 0, 1) 0ms' → * ['opacity', '200ms', 'cubic-bezier(0.4, 0, 0, 1)', '0ms'] * Splits on whitespace only when not inside parentheses. */ var tokenizeShorthand = function tokenizeShorthand(value) { var tokens = []; var depth = 0; var current = ''; for (var i = 0; i < value.length; i++) { var ch = value[i]; if (ch === '(') { depth++; current += ch; } else if (ch === ')') { depth--; current += ch; } else if (/\s/.test(ch) && depth === 0) { if (current.length > 0) { tokens.push(current); current = ''; } } else { current += ch; } } if (current.length > 0) { tokens.push(current); } return tokens; }; // Splits on top-level commas (outside function parens) — preserves cubic-bezier(...) commas. var splitOnTopLevelCommas = function splitOnTopLevelCommas(value) { var parts = []; var depth = 0; var current = ''; var _iterator = _createForOfIteratorHelper(value), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var ch = _step.value; if (ch === '(') { depth++; current += ch; } else if (ch === ')') { depth--; current += ch; } else if (ch === ',' && depth === 0) { parts.push(current.trim()); current = ''; } else { current += ch; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } if (current.trim().length > 0) { parts.push(current.trim()); } return parts; }; var parseTransition = function parseTransition(value) { var parts = tokenizeShorthand(value.trim()); var result = {}; var durationCount = 0; var _iterator2 = _createForOfIteratorHelper(parts), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var part = _step2.value; if (isDuration(part)) { if (durationCount === 0) { result.transitionDuration = part; } else { result.transitionDelay = part; } durationCount++; } else if (isEasing(part)) { result.transitionTimingFunction = part; } else { result.transitionProperty = part; } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return result; }; var parseAnimation = function parseAnimation(value) { var parts = tokenizeShorthand(value.trim()); var result = {}; var durationCount = 0; var _iterator3 = _createForOfIteratorHelper(parts), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var part = _step3.value; if (isDuration(part)) { if (durationCount === 0) { result.animationDuration = part; } else { result.animationDelay = part; } durationCount++; } else if (isEasing(part)) { result.animationTimingFunction = part; } else if (part === 'infinite' || /^\d+(\.\d+)?$/.test(part)) { result.animationIterationCount = part; } else if (['normal', 'reverse', 'alternate', 'alternate-reverse'].includes(part)) { result.animationDirection = part; } else if (['none', 'forwards', 'backwards', 'both'].includes(part)) { result.animationFillMode = part; } else if (['running', 'paused'].includes(part)) { result.animationPlayState = part; } else { result.animationName = part; } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return result; }; // Combine sub-property values across comma-separated transitions/animations. // If no segment explicitly set this sub-property, omit it entirely. // Otherwise, fill missing slots with the CSS spec default to preserve positional alignment. var combineSubPropertyValues = function combineSubPropertyValues(segments, subProperty, defaultValue) { if (segments.every(function (s) { return s[subProperty] === undefined; })) { return undefined; } return segments.map(function (s) { var _s$subProperty; return (_s$subProperty = s[subProperty]) !== null && _s$subProperty !== void 0 ? _s$subProperty : defaultValue; }).join(', '); }; var buildTransitionFix = function buildTransitionFix(segments, indent) { var lines = []; var property = combineSubPropertyValues(segments, 'transitionProperty', 'all'); var duration = combineSubPropertyValues(segments, 'transitionDuration', '0s'); var timing = combineSubPropertyValues(segments, 'transitionTimingFunction', 'ease'); var delay = combineSubPropertyValues(segments, 'transitionDelay', '0s'); if (property !== undefined) lines.push("transitionProperty: '".concat(property, "'")); if (duration !== undefined) lines.push("transitionDuration: '".concat(duration, "'")); if (timing !== undefined) lines.push("transitionTimingFunction: '".concat(timing, "'")); if (delay !== undefined) lines.push("transitionDelay: '".concat(delay, "'")); return lines.join(",\n".concat(indent)); }; var buildAnimationFix = function buildAnimationFix(segments, indent) { var lines = []; var name = combineSubPropertyValues(segments, 'animationName', 'none'); var duration = combineSubPropertyValues(segments, 'animationDuration', '0s'); var timing = combineSubPropertyValues(segments, 'animationTimingFunction', 'ease'); var delay = combineSubPropertyValues(segments, 'animationDelay', '0s'); var iter = combineSubPropertyValues(segments, 'animationIterationCount', '1'); var direction = combineSubPropertyValues(segments, 'animationDirection', 'normal'); var fill = combineSubPropertyValues(segments, 'animationFillMode', 'none'); var playState = combineSubPropertyValues(segments, 'animationPlayState', 'running'); if (name !== undefined) lines.push("animationName: '".concat(name, "'")); if (duration !== undefined) lines.push("animationDuration: '".concat(duration, "'")); if (timing !== undefined) lines.push("animationTimingFunction: '".concat(timing, "'")); if (delay !== undefined) lines.push("animationDelay: '".concat(delay, "'")); if (iter !== undefined) lines.push("animationIterationCount: '".concat(iter, "'")); if (direction !== undefined) lines.push("animationDirection: '".concat(direction, "'")); if (fill !== undefined) lines.push("animationFillMode: '".concat(fill, "'")); if (playState !== undefined) lines.push("animationPlayState: '".concat(playState, "'")); return lines.join(",\n".concat(indent)); }; var TRANSITION_SUB_PROPERTIES = ['transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay']; var ANIMATION_SUB_PROPERTIES = ['animationName', 'animationDuration', 'animationTimingFunction', 'animationDelay', 'animationIterationCount', 'animationDirection', 'animationFillMode', 'animationPlayState']; var executeExpandTransitionRule = function executeExpandTransitionRule(context, node, property) { var _context$sourceCode, _node$loc; if (node.value.type === 'CallExpression') { return; } if (node.value.type === 'TemplateLiteral') { return; } if (node.value.type !== 'Literal' || typeof node.value.value !== 'string') { return; } var rawValue = node.value.value; if (KEYWORD_VALUES.includes(rawValue)) { return; } var subProperties = property === 'transition' ? TRANSITION_SUB_PROPERTIES : ANIMATION_SUB_PROPERTIES; // Extract leading whitespace to preserve indentation style (tabs vs spaces) var sourceCode = (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode(); var nodeStart = (_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start; var indent = '\t'; if (nodeStart) { var _sourceCode$lines; var lineText = (_sourceCode$lines = sourceCode.lines[nodeStart.line - 1]) !== null && _sourceCode$lines !== void 0 ? _sourceCode$lines : ''; var leadingWhitespace = lineText.match(/^(\s*)/); if (leadingWhitespace) { indent = leadingWhitespace[1]; } } var segmentStrings = splitOnTopLevelCommas(rawValue); if (property === 'transition') { var segments = segmentStrings.map(parseTransition); var fixText = buildTransitionFix(segments, indent); context.report({ node: node, messageId: 'expandTransitionShorthand', data: { property: property, subProperties: subProperties.join(', ') }, fix: function fix(fixer) { return fixer.replaceText(node, fixText); } }); } else { var _segments = segmentStrings.map(parseAnimation); var _fixText = buildAnimationFix(_segments, indent); context.report({ node: node, messageId: 'expandTransitionShorthand', data: { property: property, subProperties: subProperties.join(', ') }, fix: function fix(fixer) { return fixer.replaceText(node, _fixText); } }); } }; var rule = createLintRule({ meta: { name: 'expand-motion-shorthand', type: 'suggestion', fixable: 'code', docs: { description: 'Expands `transition` and `animation` CSS shorthand properties into their individual sub-properties, so individual values can be replaced with motion tokens.', recommended: false, severity: 'warn' }, messages: { expandTransitionShorthand: "Use {{ subProperties }} instead of the '{{ property }}' shorthand so that individual values can be replaced with motion tokens." } }, create: function create(context) { return { 'Property[key.name="transition"]': function PropertyKeyNameTransition(node) { executeExpandTransitionRule(context, node, 'transition'); }, 'Property[key.name="animation"]': function PropertyKeyNameAnimation(node) { executeExpandTransitionRule(context, node, 'animation'); } }; } }); export default rule;