UNPKG

@hero-design/snowflake-guard

Version:

A hero-design bot detecting snowflake usage

240 lines (239 loc) 12.6 kB
"use strict"; 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;