@hero-design/snowflake-guard
Version:
A hero-design bot detecting snowflake usage
240 lines (239 loc) • 12.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ADDITIONAL_PROPERTIES = exports.INLINE_STYLE_PROPERTIES = void 0;
const recast = __importStar(require("recast"));
const constants_1 = require("./constants");
const BLACKLIST_PROPERTIES = {
style: constants_1.RULESET_MAP,
sx: constants_1.SX_RULESET_MAP,
};
exports.INLINE_STYLE_PROPERTIES = ['style', 'sx'];
exports.ADDITIONAL_PROPERTIES = ['variant']; // Add any additional props you want to track
const addViolatingAttribute = (prop, styleObjName, componentName, loc, violatingAttributes) => {
if (prop.key.type !== 'Identifier') {
return;
}
violatingAttributes.push({
attributeName: prop.key.name,
attributeValue: recast.print(prop.value).code,
inlineStyleProps: styleObjName,
componentName,
loc,
});
};
const getPropValue = (attr) => {
var _a;
if (!attr.value) {
return ''; // Handle case where value is missing
}
if (typeof attr.value === 'string') {
return attr.value; // Directly return string value
}
if (((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) === 'JSXExpressionContainer') {
return recast.print(attr.value.expression).code;
}
// Use recast to print the expression if it's not a simple string
return recast.print(attr.value).code;
};
const reportInlineStyle = (ast, attributes, componentName) => {
const locs = {};
const violatingAttributes = [];
const additionalProps = [];
let hasCustomStyle = false;
let styleObjName;
attributes.forEach((attr) => {
var _a;
if (attr.type !== 'JSXAttribute') {
return;
}
if (typeof attr.name.name !== 'string') {
return;
}
if (exports.ADDITIONAL_PROPERTIES.includes(attr.name.name)) {
// Handle expression container like `style={{ ... }}`
additionalProps.push({
propName: attr.name.name,
propValue: getPropValue(attr), // Print expression as string
});
}
if (((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) !== 'JSXExpressionContainer') {
return;
}
if (exports.INLINE_STYLE_PROPERTIES.includes(attr.name.name)) {
styleObjName = attr.name.name;
const { expression } = attr.value;
if (expression.type === 'ObjectExpression') {
expression.properties.forEach((prop) => {
var _a;
// Case 1: Use direct object, e.g. <Card style={{ color: 'red' }} />
if (prop.type === 'ObjectProperty' &&
prop.key.type === 'Identifier' &&
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(prop.key.name)) {
hasCustomStyle = true;
addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
}
// Case 2: Use spread operator, e.g. <Card style={{ ...customStyle }} />
if (prop.type === 'SpreadElement' &&
prop.argument.type === 'Identifier') {
const variableName = prop.argument.name;
recast.visit(ast, {
visitVariableDeclaration(path) {
var _a;
this.traverse(path);
const declaration = path.value
.declarations[0];
if (declaration.id.type === 'Identifier' &&
declaration.id.name === variableName &&
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
declaration.init.properties.forEach((deProp) => {
var _a;
if (deProp.type === 'ObjectProperty' &&
deProp.key.type === 'Identifier' &&
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(deProp.key.name)) {
hasCustomStyle = true;
addViolatingAttribute(deProp, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
}
});
}
},
});
}
// Case 3: Use spread operator with variable's property, e.g. <Card style={{ ...customStyle.tileCard }} />
if (prop.type === 'SpreadElement' &&
prop.argument.type === 'MemberExpression' &&
prop.argument.object.type === 'Identifier' &&
prop.argument.property.type === 'Identifier') {
const variableName = prop.argument.object.name;
const propName = prop.argument.property.name;
recast.visit(ast, {
visitVariableDeclaration(path) {
var _a;
this.traverse(path);
const declaration = path.value
.declarations[0];
if (declaration.id.type === 'Identifier' &&
declaration.id.name === variableName &&
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
declaration.init.properties.forEach((deProp) => {
if (deProp.type === 'ObjectProperty' &&
deProp.key.type === 'Identifier' &&
deProp.key.name === propName &&
deProp.value.type === 'ObjectExpression') {
deProp.value.properties.forEach((p) => {
var _a;
if (p.type === 'ObjectProperty' &&
p.key.type === 'Identifier' &&
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(p.key.name)) {
hasCustomStyle = true;
addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
}
});
}
});
}
},
});
}
});
}
// Case 4: Use variable, e.g. <Card style={customStyle} />
if (expression.type === 'Identifier') {
const variableName = expression.name;
recast.visit(ast, {
visitVariableDeclaration(path) {
var _a;
this.traverse(path);
const declaration = path.value
.declarations[0];
if (declaration.id.type === 'Identifier' &&
declaration.id.name === variableName &&
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
declaration.init.properties.forEach((prop) => {
var _a;
if (prop.type === 'ObjectProperty' &&
prop.key.type === 'Identifier' &&
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(prop.key.name)) {
hasCustomStyle = true;
addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
}
});
}
},
});
}
// Case 5: Use variable's property, e.g. <Card style={customStyle.tileCard} />
if (expression.type === 'MemberExpression' &&
expression.object.type === 'Identifier' &&
expression.property.type === 'Identifier') {
const objectName = expression.object.name;
const propName = expression.property.name;
recast.visit(ast, {
visitVariableDeclaration(path) {
var _a;
this.traverse(path);
const declaration = path.value
.declarations[0];
if (declaration.id.type === 'Identifier' &&
declaration.id.name === objectName &&
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
declaration.init.properties.forEach((prop) => {
if (prop.type === 'ObjectProperty' &&
prop.key.type === 'Identifier' &&
prop.key.name === propName &&
prop.value.type === 'ObjectExpression') {
prop.value.properties.forEach((p) => {
var _a;
if (p.type === 'ObjectProperty' &&
p.key.type === 'Identifier' &&
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(p.key.name)) {
hasCustomStyle = true;
addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
}
});
}
});
}
},
});
}
if (hasCustomStyle && attr.loc) {
locs[styleObjName] = attr.loc.start.line;
}
}
});
return { locs, violatingAttributes, additionalProps };
};
exports.default = reportInlineStyle;