1
0
mirror of https://github.com/sasjs/lint.git synced 2026-01-17 17:20:05 +00:00

feat: add new property severityLevel

This commit is contained in:
2022-11-16 22:40:17 +05:00
parent 8031468926
commit 0cff87fe12
18 changed files with 213 additions and 33 deletions

View File

@@ -159,6 +159,115 @@
"description": "An array of paths or path patterns to ignore when linting. Any files or matching patterns in the .gitignore file will also be ignored.", "description": "An array of paths or path patterns to ignore when linting. Any files or matching patterns in the .gitignore file will also be ignored.",
"default": ["sasjsbuild/", "sasjsresults/"], "default": ["sasjsbuild/", "sasjsresults/"],
"examples": ["sasjs/tests", "tmp/scratch.sas"] "examples": ["sasjs/tests", "tmp/scratch.sas"]
},
"severityLevel": {
"$id": "#/properties/severityLevel",
"type": "object",
"title": "severityLevel",
"description": "An object which specifies the severity level of each rule.",
"default": {},
"examples": [{
"hasDoxygenHeader": "warn",
"maxLineLength": "warn",
"noTrailingSpaces": "error"
}, {
"hasDoxygenHeader": "warn",
"maxLineLength": "error",
"noTrailingSpaces": "error"
}],
"properties": {
"noEncodedPasswords": {
"$id": "#/properties/severityLevel/noEncodedPasswords",
"title": "noEncodedPasswords",
"type": "string",
"enum": ["error", "warn"],
"default": "error"
},
"hasDoxygenHeader": {
"$id": "#/properties/severityLevel/hasDoxygenHeader",
"title": "hasDoxygenHeader",
"type": "string",
"enum": ["error", "warn"],
"default": "warn" },
"hasMacroNameInMend": {
"$id": "#/properties/severityLevel/hasMacroNameInMend",
"title": "hasMacroNameInMend",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"hasMacroParentheses": {
"$id": "#/properties/severityLevel/hasMacroParentheses",
"title": "hasMacroParentheses",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"indentationMultiple": {
"$id": "#/properties/severityLevel/indentationMultiple",
"title": "indentationMultiple",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"lowerCaseFileNames": {
"$id": "#/properties/severityLevel/lowerCaseFileNames",
"title": "lowerCaseFileNames",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"maxLineLength": {
"$id": "#/properties/severityLevel/maxLineLength",
"title": "maxLineLength",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"noNestedMacros": {
"$id": "#/properties/severityLevel/noNestedMacros",
"title": "noNestedMacros",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"noSpacesInFileNames": {
"$id": "#/properties/severityLevel/noSpacesInFileNames",
"title": "noSpacesInFileNames",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"noTabIndentation": {
"$id": "#/properties/severityLevel/noTabIndentation",
"title": "noTabIndentation",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"noTrailingSpaces": {
"$id": "#/properties/severityLevel/noTrailingSpaces",
"title": "noTrailingSpaces",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"lineEndings": {
"$id": "#/properties/severityLevel/lineEndings",
"title": "lineEndings",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
},
"strictMacroDefinition": {
"$id": "#/properties/severityLevel/strictMacroDefinition",
"title": "strictMacroDefinition",
"type": "string",
"enum": ["error", "warn"],
"default": "warn"
}
}
} }
} }
} }

View File

@@ -18,7 +18,7 @@ export const processFile = (
): Diagnostic[] => { ): Diagnostic[] => {
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
config.pathLintRules.forEach((rule) => { config.pathLintRules.forEach((rule) => {
diagnostics.push(...rule.test(filePath)) diagnostics.push(...rule.test(filePath, config))
}) })
return diagnostics return diagnostics
@@ -27,7 +27,7 @@ export const processFile = (
const processContent = (config: LintConfig, content: string): Diagnostic[] => { const processContent = (config: LintConfig, content: string): Diagnostic[] => {
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
config.fileLintRules.forEach((rule) => { config.fileLintRules.forEach((rule) => {
diagnostics.push(...rule.test(content)) diagnostics.push(...rule.test(content, config))
}) })
return diagnostics return diagnostics

View File

@@ -11,8 +11,11 @@ const description =
const message = 'File missing Doxygen header' const message = 'File missing Doxygen header'
const messageForSingleAsterisk = const messageForSingleAsterisk =
'File not following Doxygen header style, use double asterisks' 'File not following Doxygen header style, use double asterisks'
const test = (value: string, config?: LintConfig) => { const test = (value: string, config?: LintConfig) => {
const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n' const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n'
const severity = config?.severityLevel[name] || Severity.Warning
try { try {
const hasFileHeader = value.trimStart().startsWith('/**') const hasFileHeader = value.trimStart().startsWith('/**')
if (hasFileHeader) return [] if (hasFileHeader) return []
@@ -27,7 +30,7 @@ const test = (value: string, config?: LintConfig) => {
.length + 1, .length + 1,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
@@ -37,7 +40,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: 1, lineNumber: 1,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} catch (e) { } catch (e) {
@@ -47,7 +50,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: 1, lineNumber: 1,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} }

View File

@@ -11,11 +11,14 @@ const name = 'hasMacroNameInMend'
const description = const description =
'Enforces the presence of the macro name in each %mend statement.' 'Enforces the presence of the macro name in each %mend statement.'
const message = '%mend statement has missing or incorrect macro name' const message = '%mend statement has missing or incorrect macro name'
const test = (value: string, config?: LintConfig) => { const test = (value: string, config?: LintConfig) => {
const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n' const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n'
const lines: string[] = value ? value.split(lineEnding) : [] const lines: string[] = value ? value.split(lineEnding) : []
const macros = parseMacros(value, config) const macros = parseMacros(value, config)
const severity = config?.severityLevel[name] || Severity.Warning
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
macros.forEach((macro) => { macros.forEach((macro) => {
if (macro.startLineNumbers.length === 0 && macro.endLineNumber !== null) { if (macro.startLineNumbers.length === 0 && macro.endLineNumber !== null) {
const endLine = lines[macro.endLineNumber - 1] const endLine = lines[macro.endLineNumber - 1]
@@ -25,7 +28,7 @@ const test = (value: string, config?: LintConfig) => {
startColumnNumber: getColumnNumber(endLine, '%mend'), startColumnNumber: getColumnNumber(endLine, '%mend'),
endColumnNumber: endColumnNumber:
getColumnNumber(endLine, '%mend') + macro.termination.length, getColumnNumber(endLine, '%mend') + macro.termination.length,
severity: Severity.Warning severity
}) })
} else if ( } else if (
macro.endLineNumber === null && macro.endLineNumber === null &&
@@ -36,7 +39,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: macro.startLineNumbers![0], lineNumber: macro.startLineNumbers![0],
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
}) })
} else if (macro.mismatchedMendMacroName) { } else if (macro.mismatchedMendMacroName) {
const endLine = lines[(macro.endLineNumber as number) - 1] const endLine = lines[(macro.endLineNumber as number) - 1]
@@ -53,7 +56,7 @@ const test = (value: string, config?: LintConfig) => {
getColumnNumber(endLine, macro.mismatchedMendMacroName) + getColumnNumber(endLine, macro.mismatchedMendMacroName) +
macro.mismatchedMendMacroName.length - macro.mismatchedMendMacroName.length -
1, 1,
severity: Severity.Warning severity
}) })
} else if (!macro.hasMacroNameInMend) { } else if (!macro.hasMacroNameInMend) {
const endLine = lines[(macro.endLineNumber as number) - 1] const endLine = lines[(macro.endLineNumber as number) - 1]
@@ -62,7 +65,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: macro.endLineNumber as number, lineNumber: macro.endLineNumber as number,
startColumnNumber: getColumnNumber(endLine, '%mend'), startColumnNumber: getColumnNumber(endLine, '%mend'),
endColumnNumber: getColumnNumber(endLine, '%mend') + 6, endColumnNumber: getColumnNumber(endLine, '%mend') + 6,
severity: Severity.Warning severity
}) })
} }
}) })

View File

@@ -9,9 +9,12 @@ import { LintConfig } from '../../types'
const name = 'hasMacroParentheses' const name = 'hasMacroParentheses'
const description = 'Enforces the presence of parentheses in macro definitions.' const description = 'Enforces the presence of parentheses in macro definitions.'
const message = 'Macro definition missing parentheses' const message = 'Macro definition missing parentheses'
const test = (value: string, config?: LintConfig) => { const test = (value: string, config?: LintConfig) => {
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
const macros = parseMacros(value, config) const macros = parseMacros(value, config)
const severity = config?.severityLevel[name] || Severity.Warning
macros.forEach((macro) => { macros.forEach((macro) => {
if (!macro.name) { if (!macro.name) {
diagnostics.push({ diagnostics.push({
@@ -24,7 +27,7 @@ const test = (value: string, config?: LintConfig) => {
endColumnNumber: endColumnNumber:
getColumnNumber(macro.declarationLines![0], '%macro') + getColumnNumber(macro.declarationLines![0], '%macro') +
macro.declaration.length, macro.declaration.length,
severity: Severity.Warning severity
}) })
} else if (!macro.declarationLines.find((dl) => dl.includes('('))) { } else if (!macro.declarationLines.find((dl) => dl.includes('('))) {
const macroNameLineIndex = macro.declarationLines.findIndex((dl) => const macroNameLineIndex = macro.declarationLines.findIndex((dl) =>
@@ -44,7 +47,7 @@ const test = (value: string, config?: LintConfig) => {
) + ) +
macro.name.length - macro.name.length -
1, 1,
severity: Severity.Warning severity
}) })
} }
}) })

View File

@@ -7,6 +7,7 @@ import { Severity } from '../../types/Severity'
const name = 'lineEndings' const name = 'lineEndings'
const description = 'Ensures line endings conform to the configured type.' const description = 'Ensures line endings conform to the configured type.'
const message = 'Incorrect line ending - {actual} instead of {expected}' const message = 'Incorrect line ending - {actual} instead of {expected}'
const test = (value: string, config?: LintConfig) => { const test = (value: string, config?: LintConfig) => {
const lineEndingConfig = config?.lineEndings || LineEndings.LF const lineEndingConfig = config?.lineEndings || LineEndings.LF
const expectedLineEnding = const expectedLineEnding =
@@ -18,8 +19,10 @@ const test = (value: string, config?: LintConfig) => {
.replace(/\n/g, '{lf}') .replace(/\n/g, '{lf}')
.split(new RegExp(`(?<=${expectedLineEnding})`)) .split(new RegExp(`(?<=${expectedLineEnding})`))
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
const severity = config?.severityLevel[name] || Severity.Warning
let indexOffset = 0 let indexOffset = 0
lines.forEach((line, index) => { lines.forEach((line, index) => {
if (line.endsWith(incorrectLineEnding)) { if (line.endsWith(incorrectLineEnding)) {
diagnostics.push({ diagnostics.push({
@@ -29,7 +32,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: index + 1 + indexOffset, lineNumber: index + 1 + indexOffset,
startColumnNumber: line.indexOf(incorrectLineEnding), startColumnNumber: line.indexOf(incorrectLineEnding),
endColumnNumber: line.indexOf(incorrectLineEnding) + 1, endColumnNumber: line.indexOf(incorrectLineEnding) + 1,
severity: Severity.Warning severity
}) })
} else { } else {
const splitLine = line.split(new RegExp(`(?<=${incorrectLineEnding})`)) const splitLine = line.split(new RegExp(`(?<=${incorrectLineEnding})`))
@@ -51,7 +54,7 @@ const test = (value: string, config?: LintConfig) => {
lineNumber: index + i + 1, lineNumber: index + i + 1,
startColumnNumber: l.indexOf(incorrectLineEnding), startColumnNumber: l.indexOf(incorrectLineEnding),
endColumnNumber: l.indexOf(incorrectLineEnding) + 1, endColumnNumber: l.indexOf(incorrectLineEnding) + 1,
severity: Severity.Warning severity
}) })
} }
}) })

View File

@@ -10,11 +10,14 @@ import { LineEndings } from '../../types/LineEndings'
const name = 'noNestedMacros' const name = 'noNestedMacros'
const description = 'Enfoces the absence of nested macro definitions.' const description = 'Enfoces the absence of nested macro definitions.'
const message = `Macro definition for '{macro}' present in macro '{parent}'` const message = `Macro definition for '{macro}' present in macro '{parent}'`
const test = (value: string, config?: LintConfig) => { const test = (value: string, config?: LintConfig) => {
const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n' const lineEnding = config?.lineEndings === LineEndings.CRLF ? '\r\n' : '\n'
const lines: string[] = value ? value.split(lineEnding) : [] const lines: string[] = value ? value.split(lineEnding) : []
const diagnostics: Diagnostic[] = [] const diagnostics: Diagnostic[] = []
const macros = parseMacros(value, config) const macros = parseMacros(value, config)
const severity = config?.severityLevel[name] || Severity.Warning
macros macros
.filter((m) => !!m.parentMacro) .filter((m) => !!m.parentMacro)
.forEach((macro) => { .forEach((macro) => {
@@ -34,7 +37,7 @@ const test = (value: string, config?: LintConfig) => {
) + ) +
lines[(macro.startLineNumbers![0] as number) - 1].trim().length - lines[(macro.startLineNumbers![0] as number) - 1].trim().length -
1, 1,
severity: Severity.Warning severity
}) })
}) })
return diagnostics return diagnostics

View File

@@ -25,9 +25,11 @@ const validOptions = [
const processParams = ( const processParams = (
content: string, content: string,
macro: Macro, macro: Macro,
diagnostics: Diagnostic[] diagnostics: Diagnostic[],
config?: LintConfig
): string => { ): string => {
const declaration = macro.declaration const declaration = macro.declaration
const severity = config?.severityLevel[name] || Severity.Warning
const regExpParams = new RegExp(/(?<=\().*(?=\))/) const regExpParams = new RegExp(/(?<=\().*(?=\))/)
const regExpParamsResult = regExpParams.exec(declaration) const regExpParamsResult = regExpParams.exec(declaration)
@@ -88,7 +90,7 @@ const processParams = (
lineNumber: paramLineNumber, lineNumber: paramLineNumber,
startColumnNumber: paramStartIndex + 1, startColumnNumber: paramStartIndex + 1,
endColumnNumber: paramEndIndex, endColumnNumber: paramEndIndex,
severity: Severity.Warning severity
}) })
} }
}) })
@@ -101,9 +103,11 @@ const processParams = (
const processOptions = ( const processOptions = (
_declaration: string, _declaration: string,
macro: Macro, macro: Macro,
diagnostics: Diagnostic[] diagnostics: Diagnostic[],
config?: LintConfig
): void => { ): void => {
let optionsPresent = _declaration.split('/')?.[1]?.trim() let optionsPresent = _declaration.split('/')?.[1]?.trim()
const severity = config?.severityLevel[name] || Severity.Warning
if (optionsPresent) { if (optionsPresent) {
const regex = new RegExp(/="(.*?)"/, 'g') const regex = new RegExp(/="(.*?)"/, 'g')
@@ -136,7 +140,7 @@ const processOptions = (
startColumnNumber: declarationLine.indexOf(trimmedOption) + 1, startColumnNumber: declarationLine.indexOf(trimmedOption) + 1,
endColumnNumber: endColumnNumber:
declarationLine.indexOf(trimmedOption) + trimmedOption.length, declarationLine.indexOf(trimmedOption) + trimmedOption.length,
severity: Severity.Warning severity
}) })
} }
}) })
@@ -149,9 +153,9 @@ const test = (value: string, config?: LintConfig) => {
const macros = parseMacros(value, config) const macros = parseMacros(value, config)
macros.forEach((macro) => { macros.forEach((macro) => {
const _declaration = processParams(value, macro, diagnostics) const _declaration = processParams(value, macro, diagnostics, config)
processOptions(_declaration, macro, diagnostics) processOptions(_declaration, macro, diagnostics, config)
}) })
return diagnostics return diagnostics

View File

@@ -6,9 +6,11 @@ import { Severity } from '../../types/Severity'
const name = 'indentationMultiple' const name = 'indentationMultiple'
const description = 'Ensure indentation by a multiple of the configured number.' const description = 'Ensure indentation by a multiple of the configured number.'
const message = 'Line has incorrect indentation' const message = 'Line has incorrect indentation'
const test = (value: string, lineNumber: number, config?: LintConfig) => { const test = (value: string, lineNumber: number, config?: LintConfig) => {
if (!value.startsWith(' ')) return [] if (!value.startsWith(' ')) return []
const severity = config?.severityLevel[name] || Severity.Warning
const indentationMultiple = isNaN(config?.indentationMultiple as number) const indentationMultiple = isNaN(config?.indentationMultiple as number)
? 2 ? 2
: config!.indentationMultiple : config!.indentationMultiple
@@ -24,7 +26,7 @@ const test = (value: string, lineNumber: number, config?: LintConfig) => {
lineNumber, lineNumber,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} }

View File

@@ -6,7 +6,9 @@ import { Severity } from '../../types/Severity'
const name = 'maxLineLength' const name = 'maxLineLength'
const description = 'Restrict lines to the specified length.' const description = 'Restrict lines to the specified length.'
const message = 'Line exceeds maximum length' const message = 'Line exceeds maximum length'
const test = (value: string, lineNumber: number, config?: LintConfig) => { const test = (value: string, lineNumber: number, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning
const maxLineLength = config?.maxLineLength || 80 const maxLineLength = config?.maxLineLength || 80
if (value.length <= maxLineLength) return [] if (value.length <= maxLineLength) return []
return [ return [
@@ -15,7 +17,7 @@ const test = (value: string, lineNumber: number, config?: LintConfig) => {
lineNumber, lineNumber,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} }

View File

@@ -1,3 +1,4 @@
import { LintConfig } from '../../types'
import { LineLintRule } from '../../types/LintRule' import { LineLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType' import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity' import { Severity } from '../../types/Severity'
@@ -5,7 +6,9 @@ import { Severity } from '../../types/Severity'
const name = 'noEncodedPasswords' const name = 'noEncodedPasswords'
const description = 'Disallow encoded passwords in SAS code.' const description = 'Disallow encoded passwords in SAS code.'
const message = 'Line contains encoded password' const message = 'Line contains encoded password'
const test = (value: string, lineNumber: number) => {
const test = (value: string, lineNumber: number, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Error
const regex = new RegExp(/{sas(\d{2,4}|enc)}[^;"'\s]*/, 'gi') const regex = new RegExp(/{sas(\d{2,4}|enc)}[^;"'\s]*/, 'gi')
const matches = value.match(regex) const matches = value.match(regex)
if (!matches || !matches.length) return [] if (!matches || !matches.length) return []
@@ -14,7 +17,7 @@ const test = (value: string, lineNumber: number) => {
lineNumber, lineNumber,
startColumnNumber: value.indexOf(match) + 1, startColumnNumber: value.indexOf(match) + 1,
endColumnNumber: value.indexOf(match) + match.length + 1, endColumnNumber: value.indexOf(match) + match.length + 1,
severity: Severity.Error severity
})) }))
} }

View File

@@ -1,3 +1,4 @@
import { LintConfig } from '../../types'
import { LineLintRule } from '../../types/LintRule' import { LineLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType' import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity' import { Severity } from '../../types/Severity'
@@ -5,7 +6,9 @@ import { Severity } from '../../types/Severity'
const name = 'noTabs' const name = 'noTabs'
const description = 'Disallow indenting with tabs.' const description = 'Disallow indenting with tabs.'
const message = 'Line is indented with a tab' const message = 'Line is indented with a tab'
const test = (value: string, lineNumber: number) => {
const test = (value: string, lineNumber: number, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning
if (!value.startsWith('\t')) return [] if (!value.startsWith('\t')) return []
return [ return [
{ {

View File

@@ -1,3 +1,4 @@
import { LintConfig } from '../../types'
import { LineLintRule } from '../../types/LintRule' import { LineLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType' import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity' import { Severity } from '../../types/Severity'
@@ -5,8 +6,11 @@ import { Severity } from '../../types/Severity'
const name = 'noTrailingSpaces' const name = 'noTrailingSpaces'
const description = 'Disallow trailing spaces on lines.' const description = 'Disallow trailing spaces on lines.'
const message = 'Line contains trailing spaces' const message = 'Line contains trailing spaces'
const test = (value: string, lineNumber: number) =>
value.trimEnd() === value const test = (value: string, lineNumber: number, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning
return value.trimEnd() === value
? [] ? []
: [ : [
{ {
@@ -14,9 +18,11 @@ const test = (value: string, lineNumber: number) =>
lineNumber, lineNumber,
startColumnNumber: value.trimEnd().length + 1, startColumnNumber: value.trimEnd().length + 1,
endColumnNumber: value.length, endColumnNumber: value.length,
severity: Severity.Warning severity
} }
] ]
}
const fix = (value: string) => value.trimEnd() const fix = (value: string) => value.trimEnd()
/** /**

View File

@@ -2,20 +2,25 @@ import { PathLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType' import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity' import { Severity } from '../../types/Severity'
import path from 'path' import path from 'path'
import { LintConfig } from '../../types'
const name = 'lowerCaseFileNames' const name = 'lowerCaseFileNames'
const description = 'Enforce the use of lower case file names.' const description = 'Enforce the use of lower case file names.'
const message = 'File name contains uppercase characters' const message = 'File name contains uppercase characters'
const test = (value: string) => {
const test = (value: string, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning
const fileName = path.basename(value) const fileName = path.basename(value)
if (fileName.toLocaleLowerCase() === fileName) return [] if (fileName.toLocaleLowerCase() === fileName) return []
return [ return [
{ {
message, message,
lineNumber: 1, lineNumber: 1,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} }

View File

@@ -2,12 +2,16 @@ import { PathLintRule } from '../../types/LintRule'
import { LintRuleType } from '../../types/LintRuleType' import { LintRuleType } from '../../types/LintRuleType'
import { Severity } from '../../types/Severity' import { Severity } from '../../types/Severity'
import path from 'path' import path from 'path'
import { LintConfig } from '../../types'
const name = 'noSpacesInFileNames' const name = 'noSpacesInFileNames'
const description = 'Enforce the absence of spaces within file names.' const description = 'Enforce the absence of spaces within file names.'
const message = 'File name contains spaces' const message = 'File name contains spaces'
const test = (value: string) => {
const test = (value: string, config?: LintConfig) => {
const severity = config?.severityLevel[name] || Severity.Warning
const fileName = path.basename(value) const fileName = path.basename(value)
if (fileName.includes(' ')) { if (fileName.includes(' ')) {
return [ return [
{ {
@@ -15,7 +19,7 @@ const test = (value: string) => {
lineNumber: 1, lineNumber: 1,
startColumnNumber: 1, startColumnNumber: 1,
endColumnNumber: 1, endColumnNumber: 1,
severity: Severity.Warning severity
} }
] ]
} }

View File

@@ -1,6 +1,7 @@
import { LineEndings } from './LineEndings' import { LineEndings } from './LineEndings'
import { LintConfig } from './LintConfig' import { LintConfig } from './LintConfig'
import { LintRuleType } from './LintRuleType' import { LintRuleType } from './LintRuleType'
import { Severity } from './Severity'
describe('LintConfig', () => { describe('LintConfig', () => {
it('should create an empty instance', () => { it('should create an empty instance', () => {
@@ -123,6 +124,23 @@ describe('LintConfig', () => {
expect(config.lineEndings).toEqual(LineEndings.CRLF) expect(config.lineEndings).toEqual(LineEndings.CRLF)
}) })
it('should create an instance with the severityLevel config', () => {
const config = new LintConfig({
severityLevel: {
hasDoxygenHeader: 'warn',
maxLineLength: 'error',
noTrailingSpaces: 'error'
}
})
expect(config).toBeTruthy()
expect(config.severityLevel).toEqual({
hasDoxygenHeader: Severity.Warning,
maxLineLength: Severity.Error,
noTrailingSpaces: Severity.Error
})
})
it('should create an instance with the line endings set to LF by default', () => { it('should create an instance with the line endings set to LF by default', () => {
const config = new LintConfig({}) const config = new LintConfig({})

View File

@@ -17,6 +17,7 @@ import { lowerCaseFileNames, noSpacesInFileNames } from '../rules/path'
import { LineEndings } from './LineEndings' import { LineEndings } from './LineEndings'
import { FileLintRule, LineLintRule, PathLintRule } from './LintRule' import { FileLintRule, LineLintRule, PathLintRule } from './LintRule'
import { getDefaultHeader } from '../utils' import { getDefaultHeader } from '../utils'
import { Severity } from './Severity'
/** /**
* LintConfig is the logical representation of the .sasjslint file. * LintConfig is the logical representation of the .sasjslint file.
@@ -34,6 +35,7 @@ export class LintConfig {
readonly indentationMultiple: number = 2 readonly indentationMultiple: number = 2
readonly lineEndings: LineEndings = LineEndings.LF readonly lineEndings: LineEndings = LineEndings.LF
readonly defaultHeader: string = getDefaultHeader() readonly defaultHeader: string = getDefaultHeader()
readonly severityLevel: { [key: string]: Severity } = {}
constructor(json?: any) { constructor(json?: any) {
if (json?.ignoreList) { if (json?.ignoreList) {
@@ -116,5 +118,12 @@ export class LintConfig {
if (json?.strictMacroDefinition) { if (json?.strictMacroDefinition) {
this.fileLintRules.push(strictMacroDefinition) this.fileLintRules.push(strictMacroDefinition)
} }
if (json?.severityLevel) {
for (const [rule, severity] of Object.entries(json.severityLevel)) {
if (severity === 'warn') this.severityLevel[rule] = Severity.Warning
if (severity === 'error') this.severityLevel[rule] = Severity.Error
}
}
} }
} }

View File

@@ -36,5 +36,5 @@ export interface FileLintRule extends LintRule {
*/ */
export interface PathLintRule extends LintRule { export interface PathLintRule extends LintRule {
type: LintRuleType.Path type: LintRuleType.Path
test: (value: string) => Diagnostic[] test: (value: string, config?: LintConfig) => Diagnostic[]
} }