UNPKG

@blaze-money/eslint-plugin-spark

Version:

ESLint plugin for Spark project with custom rules

195 lines (180 loc) 7.45 kB
module.exports = { meta: { type: "problem", docs: { description: "Enforce using EnvironmentVariables enum instead of raw strings with ConfigService", category: "Best Practices", recommended: true, }, fixable: "code", hasSuggestions: true, schema: [], // no options }, create(context) { // Track if we've seen an import for EnvironmentVariables let hasEnvironmentVariablesImport = false // Track if we need to add an import and have found violations let needsImport = false // Keep track of violations for potential batch fixing let violations = [] // Store potential import locations let configImportNode = null let firstImportNode = null return { // Check for existing imports of EnvironmentVariables ImportDeclaration(node) { // Store the first import we encounter for later use if (!firstImportNode) { firstImportNode = node } if ( node.source.value === "@config.module" || node.source.value === "@app/modules/App/config.module" || node.source.value.includes("config.module") ) { // Store this as a potential location to add EnvironmentVariables import configImportNode = node // Check if EnvironmentVariables is one of the imported specifiers for (const specifier of node.specifiers) { if ( specifier.type === "ImportSpecifier" && specifier.imported && specifier.imported.name === "EnvironmentVariables" ) { hasEnvironmentVariablesImport = true break } } } }, CallExpression(node) { // Check if this is a get or get method call on config or configService if ( node.callee.type === "MemberExpression" && node.callee.property.type === "Identifier" && (node.callee.property.name === "get" || node.callee.property.name === "get") && node.callee.object.type === "MemberExpression" && node.callee.object.object.type === "ThisExpression" && node.callee.object.property.type === "Identifier" && (node.callee.object.property.name === "config" || node.callee.object.property.name === "configService") ) { // Check if we have arguments and first argument is a string literal if ( node.arguments.length > 0 && node.arguments[0].type === "Literal" && typeof node.arguments[0].value === "string" ) { const envVarName = node.arguments[0].value // Add to violations array violations.push({ node, envVarName, }) // We need to add an import if we don't have one already if (!hasEnvironmentVariablesImport) { needsImport = true } context.report({ node, message: "Use EnvironmentVariables enum instead of string literals for ConfigService.get calls", *fix(fixer) { // First replace the string literal with EnvironmentVariables.X yield fixer.replaceText( node.arguments[0], `EnvironmentVariables.${envVarName}` ) // If we need to add an import and this is the first violation being fixed if (needsImport && node === violations[0].node) { // If we found an existing config module import, add EnvironmentVariables to it if (configImportNode) { // If this is a named import with {}, add to it const hasNamedImports = configImportNode.specifiers.some( s => s.type === "ImportSpecifier" ) if (hasNamedImports) { // Find the closing brace of the import specifiers const sourceCode = context.getSourceCode() const text = sourceCode.getText(configImportNode) const openBraceIndex = text.indexOf("{") const closeBraceIndex = text.indexOf("}") if (openBraceIndex >= 0 && closeBraceIndex >= 0) { // If there are existing specifiers, add a comma const hasSpecifiers = configImportNode.specifiers.filter( s => s.type === "ImportSpecifier" ).length > 0 const insertText = hasSpecifiers ? ", EnvironmentVariables" : "EnvironmentVariables" yield fixer.insertTextBeforeRange( [ configImportNode.range[0] + closeBraceIndex, configImportNode.range[0] + closeBraceIndex, ], insertText ) } } else { // If it's a default import only, add a named import part yield fixer.insertTextAfter( configImportNode, ", { EnvironmentVariables }" ) } } else if (firstImportNode) { // Add a new import statement before the first import yield fixer.insertTextBefore( firstImportNode, `import { EnvironmentVariables } from "@app/modules/App/config.module"\n` ) } else { // No imports found, add at the beginning of the file yield fixer.insertTextBefore( context.getSourceCode().ast, `import { EnvironmentVariables } from "@app/modules/App/config.module"\n\n` ) } } }, suggest: [ { desc: `Replace with EnvironmentVariables.${envVarName}`, fix: fixer => { return fixer.replaceText( node.arguments[0], `EnvironmentVariables.${envVarName}` ) }, }, { desc: "Add EnvironmentVariables import and replace", fix: fixer => { const fixes = [ fixer.replaceText( node.arguments[0], `EnvironmentVariables.${envVarName}` ), ] if (!hasEnvironmentVariablesImport && firstImportNode) { fixes.push( fixer.insertTextBefore( firstImportNode, `import { EnvironmentVariables } from "@app/modules/App/config.module"\n` ) ) } return fixes }, }, ], }) } } }, } }, }