mirror of
https://github.com/sasjs/lint.git
synced 2026-01-14 15:50:05 +00:00
Merge pull request #198 from sasjs/issue-197
feat: add a new config attribute for allowedGremlins
This commit is contained in:
15
README.md
15
README.md
@@ -46,9 +46,22 @@ Each setting can have three states:
|
|||||||
|
|
||||||
For more details, and the default state, see the description of each rule below. It is also possible to change whether a rule returns ERROR or WARN using the `severityLevels` object.
|
For more details, and the default state, see the description of each rule below. It is also possible to change whether a rule returns ERROR or WARN using the `severityLevels` object.
|
||||||
|
|
||||||
|
### allowedGremlins
|
||||||
|
|
||||||
|
An array of hex codes that represents allowed gremlins (invisible / undesirable characters). To allow all gremlins, you can also set the `noGremlins` rule to `false`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"noGremlins": true,
|
||||||
|
"allowedGremlins": ["0x0080", "0x3000"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### defaultHeader
|
### defaultHeader
|
||||||
|
|
||||||
This isn't actually a rule - but rather a formatting setting, which applies to SAS program that do NOT begin with `/**`. It can be triggered by running `sasjs lint fix` in the SASjs CLI, or by hitting "save" when using the SASjs VS Code extension (with "formatOnSave" in place)
|
This isn't a rule, but a formatting setting, which applies to SAS program that do NOT begin with `/**`. It can be triggered by running `sasjs lint fix` in the SASjs CLI, or by hitting "save" when using the SASjs VS Code extension (with "formatOnSave" in place)
|
||||||
|
|
||||||
The default header is as follows:
|
The default header is as follows:
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"lowerCaseFileNames": true,
|
"lowerCaseFileNames": true,
|
||||||
"maxLineLength": 80,
|
"maxLineLength": 80,
|
||||||
"noGremlins": true,
|
"noGremlins": true,
|
||||||
|
"allowedGremlins": ["0x0080", "0x3000"],
|
||||||
"noTabs": true,
|
"noTabs": true,
|
||||||
"indentationMultiple": 4,
|
"indentationMultiple": 4,
|
||||||
"hasMacroNameInMend": true,
|
"hasMacroNameInMend": true,
|
||||||
@@ -74,6 +75,18 @@
|
|||||||
"default": [true],
|
"default": [true],
|
||||||
"examples": [true, false]
|
"examples": [true, false]
|
||||||
},
|
},
|
||||||
|
"allowedGremlins": {
|
||||||
|
"$id": "#/properties/allowedGremlins",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^0x[0-9A-Fa-f]{4}$"
|
||||||
|
},
|
||||||
|
"title": "allowedGremlins",
|
||||||
|
"description": "An array of hex codes that represents allowed gremlins.",
|
||||||
|
"default": [],
|
||||||
|
"examples": ["0x0080", "0x3000"]
|
||||||
|
},
|
||||||
"hasMacroNameInMend": {
|
"hasMacroNameInMend": {
|
||||||
"$id": "#/properties/hasMacroNameInMend",
|
"$id": "#/properties/hasMacroNameInMend",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Severity } from '../../types/Severity'
|
import { noGremlins, charFromHex } from './noGremlins'
|
||||||
import { noGremlins } from './noGremlins'
|
import { LintConfig } from '../../types'
|
||||||
|
|
||||||
describe('noTabs', () => {
|
describe('noTabs', () => {
|
||||||
it('should return an empty array when the line does not have any gremlin', () => {
|
it('should return an empty array when the line does not have any gremlin', () => {
|
||||||
@@ -8,8 +8,19 @@ describe('noTabs', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should return a diagnostic array when the line contains gremlins', () => {
|
it('should return a diagnostic array when the line contains gremlins', () => {
|
||||||
const line = "– ‘ %put 'hello';"
|
const line = `${charFromHex('0x0080')} ${charFromHex(
|
||||||
|
'0x3000'
|
||||||
|
)} %put 'hello';`
|
||||||
const diagnostics = noGremlins.test(line, 1)
|
const diagnostics = noGremlins.test(line, 1)
|
||||||
expect(diagnostics.length).toEqual(2)
|
expect(diagnostics.length).toEqual(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should return an empty array when the line contains gremlins but those gremlins are allowed', () => {
|
||||||
|
const config = new LintConfig({ allowedGremlins: ['0x0080', '0x3000'] })
|
||||||
|
const line = `${charFromHex('0x0080')} ${charFromHex(
|
||||||
|
'0x3000'
|
||||||
|
)} %put 'hello';`
|
||||||
|
const diagnostics = noGremlins.test(line, 1, config)
|
||||||
|
expect(diagnostics.length).toEqual(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,15 +10,18 @@ const message = 'Line contains a gremlin'
|
|||||||
|
|
||||||
const test = (value: string, lineNumber: number, config?: LintConfig) => {
|
const test = (value: string, lineNumber: number, config?: LintConfig) => {
|
||||||
const severity = config?.severityLevel[name] || Severity.Warning
|
const severity = config?.severityLevel[name] || Severity.Warning
|
||||||
|
const allowedGremlins = config?.allowedGremlins || []
|
||||||
|
|
||||||
const diagnostics: Diagnostic[] = []
|
const diagnostics: Diagnostic[] = []
|
||||||
|
|
||||||
const gremlins: any = {}
|
const gremlins: any = {}
|
||||||
|
|
||||||
for (const [hexCode, config] of Object.entries(gremlinCharacters)) {
|
for (const [hexCode, gremlinConfig] of Object.entries(gremlinCharacters)) {
|
||||||
gremlins[charFromHex(hexCode)] = Object.assign({}, config, {
|
if (!allowedGremlins.includes(hexCode)) {
|
||||||
hexCode
|
gremlins[charFromHex(hexCode)] = Object.assign({}, gremlinConfig, {
|
||||||
})
|
hexCode
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const regexpWithAllChars = new RegExp(
|
const regexpWithAllChars = new RegExp(
|
||||||
@@ -56,4 +59,5 @@ export const noGremlins: LineLintRule = {
|
|||||||
test
|
test
|
||||||
}
|
}
|
||||||
|
|
||||||
const charFromHex = (hexCode: string) => String.fromCodePoint(parseInt(hexCode))
|
export const charFromHex = (hexCode: string) =>
|
||||||
|
String.fromCodePoint(parseInt(hexCode))
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { Severity } from './Severity'
|
|||||||
*/
|
*/
|
||||||
export class LintConfig {
|
export class LintConfig {
|
||||||
readonly ignoreList: string[] = []
|
readonly ignoreList: string[] = []
|
||||||
|
readonly allowedGremlins: string[] = []
|
||||||
readonly lineLintRules: LineLintRule[] = []
|
readonly lineLintRules: LineLintRule[] = []
|
||||||
readonly fileLintRules: FileLintRule[] = []
|
readonly fileLintRules: FileLintRule[] = []
|
||||||
readonly pathLintRules: PathLintRule[] = []
|
readonly pathLintRules: PathLintRule[] = []
|
||||||
@@ -123,6 +124,23 @@ export class LintConfig {
|
|||||||
|
|
||||||
if (json?.noGremlins !== false) {
|
if (json?.noGremlins !== false) {
|
||||||
this.lineLintRules.push(noGremlins)
|
this.lineLintRules.push(noGremlins)
|
||||||
|
|
||||||
|
if (json?.allowedGremlins) {
|
||||||
|
if (Array.isArray(json.allowedGremlins)) {
|
||||||
|
json.allowedGremlins.forEach((item: any) => {
|
||||||
|
if (typeof item === 'string' && /^0x[0-9a-f]{4}$/i.test(item))
|
||||||
|
this.allowedGremlins.push(item)
|
||||||
|
else
|
||||||
|
throw new Error(
|
||||||
|
`Property "allowedGremlins" has invalid type of values. It can contain only strings of form hexcode like '["0x0080", "0x3000"]'`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Property "allowedGremlins" can only be an array of strings of form hexcode like '["0x0080", "0x3000"]'`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json?.severityLevel) {
|
if (json?.severityLevel) {
|
||||||
|
|||||||
Reference in New Issue
Block a user