UNPKG

@gitlab/eslint-plugin

Version:

GitLab package for our custom eslint rules

128 lines (103 loc) 2.79 kB
const ERROR_MESSAGE = 'Avoid hardcoded URLs. See https://docs.gitlab.com/development/urls_in_gitlab/.'; function traverse(node, type) { if (node.type === type) { return node; } if (!node.parent) { return node; } return traverse(node.parent, type); } function literalValidator(context, node) { if (isAllowedNode(context, node)) { return; } if (isHardCodedUrl(context, node.value)) { context.report({ node, message: ERROR_MESSAGE, }); } } function templateLiteralValidator(context, node) { if (isAllowedNode(context, node)) { return; } if (isConstructedUrl(context, node)) { context.report({ node, message: ERROR_MESSAGE, }); } } function memberExpressionValidator(context, node) { const { disallowedObjectProperties = [] } = context.options[0] || {}; if (disallowedObjectProperties.includes(node.property?.name)) { context.report({ node, message: ERROR_MESSAGE, }); } } function isAllowedNode(context, node) { const { parent } = node; const { allowedKeys = [], allowedFunctions = [], allowedVueComponents = [], } = context.options[0] || {}; if ( parent?.type === 'Property' && parent?.key?.type === 'Identifier' && allowedKeys.includes(parent?.key?.name) ) { return true; } if ( parent?.type === 'CallExpression' && parent?.callee?.type === 'Identifier' && allowedFunctions.includes(parent?.callee?.name) ) { return true; } const vElement = traverse(node, 'VElement'); if (vElement && allowedVueComponents.includes(vElement.name)) { return true; } return false; } /** * Check if a string looks like a hardcoded URL path */ function isHardCodedUrl(context, value) { if (typeof value !== 'string') return false; const { allowedPatterns = [] } = context.options[0] || {}; if (allowedPatterns.some((pattern) => new RegExp(pattern).test(value))) { return false; } // Check for path-like patterns: // Starts with / followed by word characters const urlPathPattern = /^\/(?![#?])[\/\.a-zA-Z0-9_-]+/; return urlPathPattern.test(value); } /** * Check if template literal looks like URL construction */ function isConstructedUrl(context, node) { const { allowedInterpolationVariables = [] } = context.options[0] || {}; if ( node.expressions.some((expression) => allowedInterpolationVariables.includes(expression.name)) ) { return false; } // Check if any quasi (string part) looks like a hard coded URL const hasHardCodedParts = node.quasis.some((quasi) => isHardCodedUrl(context, quasi.value.raw)); return hasHardCodedParts; } module.exports = { literalValidator, templateLiteralValidator, memberExpressionValidator, ERROR_MESSAGE, };