@atlaskit/eslint-plugin-ui-styling-standard
Version:
The eslint plugin to enforce and educate on Atlassian's UI Styling Standard
206 lines (204 loc) • 4.89 kB
JavaScript
/**
* This util is intentionally not in `@atlaskit/eslint-utils` at this time.
*
* This is because the API is likely to change and should not be considered stable.
*/
/**
* At-rules that are allowed to be used in "grouped" form within cssMap.
*
* "Grouped" refers to the legacy Compiled syntax where an at-rule key with no parameters
* (matching `/^@[A-z-]+$/`) contains nested variant keys, like:
*
* ```typescript
* // ❌ GROUPED (not allowed for @media):
* cssMap({
* variant: {
* '@media': { // at-rule key with no parameters
* '(min-width: 900px)': {}, // nested variants
* '(min-width: 1200px)': {},
* }
* }
* })
*
* // ✅ FLAT (preferred for @media):
* cssMap({
* variant: {
* '@media (min-width: 900px)': {}, // at-rule with parameters
* '@media (min-width: 1200px)': {},
* }
* })
* ```
*
* However, some at-rules like `@starting-style` have no parameters and can only be
* written in the "grouped" form at the variant level:
*
* ```typescript
* // ✅ ALLOWED (only valid syntax for @starting-style):
* cssMap({
* fade: {
* '@starting-style': { // at-rule with no parameters
* opacity: 0, // styles to apply
* }
* }
* })
* ```
*
* Note: This only applies to at-rules at the variant level. Nested at-rules inside
* other rulesets (e.g., `@starting-style` inside `@media (...)`) are handled by
* `walkStyleRuleset` and are not considered "grouped".
*/
const allowedGroupedAtRules = new Set(['@starting-style' // Has no parameters, can only be written in grouped form
]);
export function walkCssMapCall(call, visitor) {
const variantMap = call.arguments[0];
if ((variantMap === null || variantMap === void 0 ? void 0 : variantMap.type) === 'ObjectExpression') {
walkCssMapVariantMap({
variantMap,
visitor
});
}
}
function walkCssMapVariantMap({
variantMap,
visitor
}) {
variantMap.properties.forEach(property => {
if (property.type !== 'Property') {
return;
}
walkCssMapVariant({
variant: property,
visitor
});
});
}
function walkCssMapVariant({
variant,
visitor
}) {
visitor({
type: 'variant',
node: variant
});
if (variant.value.type !== 'ObjectExpression') {
return;
}
variant.value.properties.forEach(property => {
if (property.type !== 'Property') {
return;
}
if (property.value.type !== 'ObjectExpression') {
visitor({
type: 'declaration',
node: property
});
return;
}
const keyValue = getKeyValue(property.key);
if (keyValue && /^@[A-z-]+$/.test(keyValue)) {
walkCssMapAtRuleGrouping({
atRuleGrouping: property,
visitor
});
return;
}
if (keyValue === 'selectors') {
walkCssMapSelectors({
selectors: property,
visitor
});
}
walkStyleRuleset({
ruleset: property,
visitor
});
});
}
function walkCssMapAtRuleGrouping({
atRuleGrouping,
visitor
}) {
const atRule = getKeyValue(atRuleGrouping.key);
if (!atRule) {
return;
}
// Skip reporting if the at-rule is allowed to be grouped
if (!allowedGroupedAtRules.has(atRule)) {
visitor({
type: 'grouped-at-rules',
node: atRuleGrouping,
atRule
});
}
if (atRuleGrouping.value.type !== 'ObjectExpression') {
return;
}
atRuleGrouping.value.properties.forEach(property => {
if (property.type !== 'Property') {
return;
}
walkStyleRuleset({
ruleset: property,
visitor
});
});
}
function walkCssMapSelectors({
selectors,
visitor
}) {
visitor({
type: 'selectors',
node: selectors
});
if (selectors.value.type !== 'ObjectExpression') {
return;
}
selectors.value.properties.forEach(property => {
if (property.type !== 'Property') {
return;
}
walkStyleRuleset({
ruleset: property,
visitor
});
});
}
function walkStyleRuleset({
ruleset,
visitor
}) {
visitor({
type: 'ruleset',
node: ruleset
});
if (ruleset.value.type !== 'ObjectExpression') {
return;
}
ruleset.value.properties.forEach(property => {
if (property.type !== 'Property') {
return;
}
if (property.value.type === 'ObjectExpression') {
walkStyleRuleset({
ruleset: property,
visitor
});
return;
}
visitor({
type: 'declaration',
node: property
});
});
}
function getKeyValue(key) {
if (key.type === 'Identifier') {
return key.name;
}
if (key.type === 'Literal') {
var _key$value$toString, _key$value;
return (_key$value$toString = (_key$value = key.value) === null || _key$value === void 0 ? void 0 : _key$value.toString()) !== null && _key$value$toString !== void 0 ? _key$value$toString : null;
}
return null;
}