mirror of
https://github.com/sasjs/lint.git
synced 2025-12-11 01:44:36 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ce33ab66c | ||
| 5290339c9e | |||
| 4772aa70c6 | |||
| 623d4df79d | |||
| 40aea383b7 | |||
| e1bcf5b06b | |||
|
|
51c6dd7c1a | ||
|
|
6e0f1c4167 | ||
|
|
5f905c88d9 | ||
|
|
ac95546910 | ||
|
|
7a00cc5f2d | ||
|
|
8950c97f84 | ||
|
|
49b124e5b8 | ||
|
|
1b15938477 | ||
|
|
f6fa20af1c | ||
|
|
cf5a0700f2 | ||
|
|
0dca988438 | ||
|
|
00dafa5bc0 | ||
|
|
39bffd39a4 | ||
|
|
ec95a798b7 | ||
|
|
acfc559f25 | ||
|
|
d204b5bac6 | ||
|
|
5602063879 | ||
|
|
31cee0af91 | ||
|
|
cd91780cf5 | ||
|
|
108bbfbaa5 | ||
|
|
f2edf1176a | ||
|
|
b5d446adc9 | ||
|
|
cc221bccc3 | ||
|
|
f38bcec582 | ||
|
|
75ab01cccf |
113
.all-contributorsrc
Normal file
113
.all-contributorsrc
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"README.md"
|
||||||
|
],
|
||||||
|
"imageSize": 100,
|
||||||
|
"commit": false,
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"login": "Carus11",
|
||||||
|
"name": "Carus Kyle",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/4925828?v=4",
|
||||||
|
"profile": "https://github.com/Carus11",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "allanbowe",
|
||||||
|
"name": "Allan Bowe",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/4420615?v=4",
|
||||||
|
"profile": "https://github.com/allanbowe",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"review",
|
||||||
|
"video",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "YuryShkoda",
|
||||||
|
"name": "Yury Shkoda",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/25773492?v=4",
|
||||||
|
"profile": "https://www.erudicat.com/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"projectManagement",
|
||||||
|
"video",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "krishna-acondy",
|
||||||
|
"name": "Krishna Acondy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/2980428?v=4",
|
||||||
|
"profile": "https://krishna-acondy.io/",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"review",
|
||||||
|
"infra",
|
||||||
|
"platform",
|
||||||
|
"maintenance",
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "saadjutt01",
|
||||||
|
"name": "Muhammad Saad ",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/8914650?v=4",
|
||||||
|
"profile": "https://github.com/saadjutt01",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"review",
|
||||||
|
"mentoring",
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "sabhas",
|
||||||
|
"name": "Sabir Hassan",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/82647447?v=4",
|
||||||
|
"profile": "https://github.com/sabhas",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"review",
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "medjedovicm",
|
||||||
|
"name": "Mihajlo Medjedovic",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/18329105?v=4",
|
||||||
|
"profile": "https://github.com/medjedovicm",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"test",
|
||||||
|
"review",
|
||||||
|
"infra"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "VladislavParhomchik",
|
||||||
|
"name": "Vladislav Parhomchik",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/83717836?v=4",
|
||||||
|
"profile": "https://github.com/VladislavParhomchik",
|
||||||
|
"contributions": [
|
||||||
|
"test",
|
||||||
|
"review"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contributorsPerLine": 7,
|
||||||
|
"projectName": "lint",
|
||||||
|
"projectOwner": "sasjs",
|
||||||
|
"repoType": "github",
|
||||||
|
"repoHost": "https://github.com",
|
||||||
|
"skipCi": true,
|
||||||
|
"commitConvention": "none"
|
||||||
|
}
|
||||||
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: monthly
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
|||||||
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@@ -13,14 +13,15 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x]
|
node-version: [lts/*]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: npm
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Check Code Style
|
- name: Check Code Style
|
||||||
|
|||||||
54
CONTRIBUTING.md
Normal file
54
CONTRIBUTING.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
Contributions to `@sasjs/lint` are very welcome!
|
||||||
|
Please fill in the pull request template and make sure that your code changes are adequately covered with tests when making a PR.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
This project implements a number of rules for SAS projects and code. There are three types of rules:
|
||||||
|
|
||||||
|
* File rules - rules applied at the file level
|
||||||
|
* Line rules - rules applied to each line of a file
|
||||||
|
* Path rules - rules applied to paths and file names
|
||||||
|
|
||||||
|
When implementing a new rule, place it in the appropriate folder for its type.
|
||||||
|
Please also make sure to export it from the `index.ts` file in that folder.
|
||||||
|
|
||||||
|
The file for each rule typically exports an object that conforms to the `LintRule` interface.
|
||||||
|
This means it will have a `type`, `name`, `description` and `message` at a minimum.
|
||||||
|
|
||||||
|
File, line and path lint rules also have a `test` property.
|
||||||
|
This is a function that will run a piece of logic against the supplied item and produce an array of `Diagnostic` objects.
|
||||||
|
These objects can be used in the consuming application to display the problems in the code.
|
||||||
|
|
||||||
|
With some lint rules, we can also write logic that can automatically fix the issues found.
|
||||||
|
These rules will also have a `fix` property, which is a function that takes the original content -
|
||||||
|
either a line or the entire contents of a file, and returns the transformed content with the fix applied.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Testing is one of the most important steps when developing a new lint rule.
|
||||||
|
It helps us ensure that our lint rules do what they are intended to do.
|
||||||
|
|
||||||
|
We use `jest` for testing, and since most of the code is based on pure functions, there is little mocking to do.
|
||||||
|
This makes `@sasjs/lint` very easy to unit test, and so there is no excuse for not testing a new rule. :)
|
||||||
|
|
||||||
|
When adding a new rule, please make sure that all positive and negative scenarios are tested in separate test cases.
|
||||||
|
When modifying an existing rule, ensure that your changes haven't affected existing functionality by running the tests on your machine.
|
||||||
|
|
||||||
|
You can run the tests using `npm test`.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
This repository uses `Prettier` to ensure a uniform code style.
|
||||||
|
If you are using VS Code for development, you can automatically fix your code to match the style as follows:
|
||||||
|
|
||||||
|
- Install the `Prettier` extension for VS Code.
|
||||||
|
- Open your `settings.json` file by choosing 'Preferences: Open Settings (JSON)' from the command palette.
|
||||||
|
- Add the following items to the JSON.
|
||||||
|
```
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using another editor, or are unable to install the extension, you can run `npm run lint:fix` to fix the formatting after you've made your changes.
|
||||||
38
README.md
38
README.md
@@ -1,4 +1,9 @@
|
|||||||
[](https://dependabot.com)
|
[](/LICENSE)
|
||||||
|

|
||||||
|
[](https://github.com/sasjs/lint/issues?q=is%3Aissue+is%3Aclosed)
|
||||||
|
[](https://github.com/sasjs/lint/issues)
|
||||||
|

|
||||||
|
[](https://gitpod.io/#https://github.com/sasjs/lint)
|
||||||
|
|
||||||
# SAS Code linting and formatting
|
# SAS Code linting and formatting
|
||||||
|
|
||||||
@@ -145,3 +150,34 @@ The SAS 9 Health Check is a 'plug & play' product, that uses the [SAS 9 REST API
|
|||||||
Contact [Allan Bowe](https://www.linkedin.com/in/allanbowe/) for further details.
|
Contact [Allan Bowe](https://www.linkedin.com/in/allanbowe/) for further details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Contributors ✨
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
|
[](#contributors-)
|
||||||
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/Carus11"><img src="https://avatars.githubusercontent.com/u/4925828?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Carus Kyle</b></sub></a><br /><a href="#ideas-Carus11" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/allanbowe"><img src="https://avatars.githubusercontent.com/u/4420615?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Allan Bowe</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=allanbowe" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=allanbowe" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3Aallanbowe" title="Reviewed Pull Requests">👀</a> <a href="#video-allanbowe" title="Videos">📹</a> <a href="https://github.com/sasjs/lint/commits?author=allanbowe" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://www.erudicat.com/"><img src="https://avatars.githubusercontent.com/u/25773492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yury Shkoda</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=YuryShkoda" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=YuryShkoda" title="Tests">⚠️</a> <a href="#projectManagement-YuryShkoda" title="Project Management">📆</a> <a href="#video-YuryShkoda" title="Videos">📹</a> <a href="https://github.com/sasjs/lint/commits?author=YuryShkoda" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://krishna-acondy.io/"><img src="https://avatars.githubusercontent.com/u/2980428?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Acondy</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=krishna-acondy" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=krishna-acondy" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3Akrishna-acondy" title="Reviewed Pull Requests">👀</a> <a href="#infra-krishna-acondy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#platform-krishna-acondy" title="Packaging/porting to new platform">📦</a> <a href="#maintenance-krishna-acondy" title="Maintenance">🚧</a> <a href="#content-krishna-acondy" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/saadjutt01"><img src="https://avatars.githubusercontent.com/u/8914650?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muhammad Saad </b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=saadjutt01" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=saadjutt01" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3Asaadjutt01" title="Reviewed Pull Requests">👀</a> <a href="#mentoring-saadjutt01" title="Mentoring">🧑🏫</a> <a href="https://github.com/sasjs/lint/commits?author=saadjutt01" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/sabhas"><img src="https://avatars.githubusercontent.com/u/82647447?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabir Hassan</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=sabhas" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=sabhas" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3Asabhas" title="Reviewed Pull Requests">👀</a> <a href="#ideas-sabhas" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/medjedovicm"><img src="https://avatars.githubusercontent.com/u/18329105?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihajlo Medjedovic</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=medjedovicm" title="Code">💻</a> <a href="https://github.com/sasjs/lint/commits?author=medjedovicm" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3Amedjedovicm" title="Reviewed Pull Requests">👀</a> <a href="#infra-medjedovicm" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/lint/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/lint/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||||
|
|||||||
16
checkNodeVersion.js
Normal file
16
checkNodeVersion.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const result = process.versions
|
||||||
|
if (result && result.node) {
|
||||||
|
if (parseInt(result.node) < 14) {
|
||||||
|
console.log(
|
||||||
|
'\x1b[31m%s\x1b[0m',
|
||||||
|
`❌ Process failed due to Node Version,\nPlease install and use Node Version >= 14\nYour current Node Version is: ${result.node}`
|
||||||
|
)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
'\x1b[31m%s\x1b[0m',
|
||||||
|
'Something went wrong while checking Node version'
|
||||||
|
)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
6935
package-lock.json
generated
6935
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -4,11 +4,13 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --coverage",
|
"test": "jest --coverage",
|
||||||
"build": "rimraf build && tsc",
|
"build": "rimraf build && tsc",
|
||||||
|
"preinstall": "node checkNodeVersion",
|
||||||
|
"prebuild": "node checkNodeVersion",
|
||||||
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build && rm -rf ./src && rm tsconfig.json",
|
"prepublishOnly": "cp -r ./build/* . && rm -rf ./build && rm -rf ./src && rm tsconfig.json",
|
||||||
"postpublish": "git clean -fd",
|
"postpublish": "git clean -fd",
|
||||||
"package:lib": "npm run build && cp ./package.json build && cp README.md build && cd build && npm version \"5.0.0\" && npm pack",
|
"package:lib": "npm run build && cp ./package.json ./checkNodeVersion.js build && cp README.md build && cd build && npm version \"5.0.0\" && npm pack",
|
||||||
"lint:fix": "npx prettier --write '{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
|
"lint:fix": "npx prettier --write \"{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
||||||
"lint": "npx prettier --check '{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}'",
|
"lint": "npx prettier --check \"{src,test}/**/*.{ts,tsx,js,jsx,html,css,sass,less,json,yml,md,graphql}\"",
|
||||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -39,12 +41,14 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^26.0.23",
|
||||||
"@types/node": "^15.12.2",
|
"@types/node": "^15.12.2",
|
||||||
|
"all-contributors-cli": "^6.20.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^26.6.3",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-jest": "^26.5.6",
|
"ts-jest": "^26.5.6",
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "^2.19.0"
|
"@sasjs/utils": "^2.19.0",
|
||||||
|
"ignore": "^5.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,14 @@
|
|||||||
"description": "Enforces Macro Definition syntax. Shows a warning when incorrect syntax is used.",
|
"description": "Enforces Macro Definition syntax. Shows a warning when incorrect syntax is used.",
|
||||||
"default": true,
|
"default": true,
|
||||||
"examples": [true, false]
|
"examples": [true, false]
|
||||||
|
},
|
||||||
|
"ignoreList": {
|
||||||
|
"$id": "#/properties/ignoreList",
|
||||||
|
"type": "object",
|
||||||
|
"title": "ignoreList",
|
||||||
|
"description": "An array of paths or path patterns to ignore matching resources from linting. Files or folders matching patterns in .gitignore will always be ignored.",
|
||||||
|
"default": ["sasjsbuild/", "sasjsresults/"],
|
||||||
|
"examples": ["sasjs/services", "appinit.sas"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { readFile } from '@sasjs/utils/file'
|
import { readFile } from '@sasjs/utils/file'
|
||||||
import { LintConfig } from '../types/LintConfig'
|
import { Diagnostic, LintConfig } from '../types'
|
||||||
import { getLintConfig } from '../utils/getLintConfig'
|
import { getLintConfig, isIgnored } from '../utils'
|
||||||
import { processFile, processText } from './shared'
|
import { processFile, processText } from './shared'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyses and produces a set of diagnostics for the file at the given path.
|
* Analyses and produces a set of diagnostics for the file at the given path.
|
||||||
* @param {string} filePath - the path to the file to be linted.
|
* @param {string} filePath - the path to the file to be linted.
|
||||||
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
* @param {LintConfig} configuration - an optional configuration. When not passed in, this is read from the .sasjslint file.
|
||||||
* @returns {Diagnostic[]} array of diagnostic objects, each containing a warning, line number and column number.
|
* @returns {Promise<Diagnostic[]>} array of diagnostic objects, each containing a warning, line number and column number.
|
||||||
*/
|
*/
|
||||||
export const lintFile = async (
|
export const lintFile = async (
|
||||||
filePath: string,
|
filePath: string,
|
||||||
configuration?: LintConfig
|
configuration?: LintConfig
|
||||||
) => {
|
): Promise<Diagnostic[]> => {
|
||||||
|
if (await isIgnored(filePath)) return []
|
||||||
|
|
||||||
const config = configuration || (await getLintConfig())
|
const config = configuration || (await getLintConfig())
|
||||||
const text = await readFile(filePath)
|
const text = await readFile(filePath)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { listSubFoldersInFolder } from '@sasjs/utils/file'
|
import { listSubFoldersInFolder } from '@sasjs/utils/file'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Diagnostic } from '../types/Diagnostic'
|
import { Diagnostic, LintConfig } from '../types'
|
||||||
import { LintConfig } from '../types/LintConfig'
|
import { asyncForEach, getLintConfig, isIgnored, listSasFiles } from '../utils'
|
||||||
import { asyncForEach } from '../utils/asyncForEach'
|
|
||||||
import { getLintConfig } from '../utils/getLintConfig'
|
|
||||||
import { listSasFiles } from '../utils/listSasFiles'
|
|
||||||
import { lintFile } from './lintFile'
|
import { lintFile } from './lintFile'
|
||||||
|
|
||||||
const excludeFolders = [
|
const excludeFolders = [
|
||||||
@@ -28,6 +25,9 @@ export const lintFolder = async (
|
|||||||
) => {
|
) => {
|
||||||
const config = configuration || (await getLintConfig())
|
const config = configuration || (await getLintConfig())
|
||||||
let diagnostics: Map<string, Diagnostic[]> = new Map<string, Diagnostic[]>()
|
let diagnostics: Map<string, Diagnostic[]> = new Map<string, Diagnostic[]>()
|
||||||
|
|
||||||
|
if (await isIgnored(folderPath)) return diagnostics
|
||||||
|
|
||||||
const fileNames = await listSasFiles(folderPath)
|
const fileNames = await listSasFiles(folderPath)
|
||||||
await asyncForEach(fileNames, async (fileName) => {
|
await asyncForEach(fileNames, async (fileName) => {
|
||||||
const filePath = path.join(folderPath, fileName)
|
const filePath = path.join(folderPath, fileName)
|
||||||
@@ -39,10 +39,8 @@ export const lintFolder = async (
|
|||||||
)
|
)
|
||||||
|
|
||||||
await asyncForEach(subFolders, async (subFolder) => {
|
await asyncForEach(subFolders, async (subFolder) => {
|
||||||
const subFolderDiagnostics = await lintFolder(
|
const subFolderPath = path.join(folderPath, subFolder)
|
||||||
path.join(folderPath, subFolder),
|
const subFolderDiagnostics = await lintFolder(subFolderPath, config)
|
||||||
config
|
|
||||||
)
|
|
||||||
diagnostics = new Map([...diagnostics, ...subFolderDiagnostics])
|
diagnostics = new Map([...diagnostics, ...subFolderDiagnostics])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { FileLintRule, LineLintRule, PathLintRule } from './LintRule'
|
|||||||
* More types of rules, when available, will be added here.
|
* More types of rules, when available, will be added here.
|
||||||
*/
|
*/
|
||||||
export class LintConfig {
|
export class LintConfig {
|
||||||
|
readonly ignoreList: string[] = []
|
||||||
readonly lineLintRules: LineLintRule[] = []
|
readonly lineLintRules: LineLintRule[] = []
|
||||||
readonly fileLintRules: FileLintRule[] = []
|
readonly fileLintRules: FileLintRule[] = []
|
||||||
readonly pathLintRules: PathLintRule[] = []
|
readonly pathLintRules: PathLintRule[] = []
|
||||||
@@ -33,6 +34,20 @@ export class LintConfig {
|
|||||||
readonly lineEndings: LineEndings = LineEndings.LF
|
readonly lineEndings: LineEndings = LineEndings.LF
|
||||||
|
|
||||||
constructor(json?: any) {
|
constructor(json?: any) {
|
||||||
|
if (json?.ignoreList) {
|
||||||
|
if (Array.isArray(json.ignoreList)) {
|
||||||
|
json.ignoreList.forEach((item: any) => {
|
||||||
|
if (typeof item === 'string') this.ignoreList.push(item)
|
||||||
|
else
|
||||||
|
throw new Error(
|
||||||
|
`Property "ignoreList" has invalid type of values. It can contain only strings.`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(`Property "ignoreList" can only be an array of strings`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (json?.noTrailingSpaces) {
|
if (json?.noTrailingSpaces) {
|
||||||
this.lineLintRules.push(noTrailingSpaces)
|
this.lineLintRules.push(noTrailingSpaces)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
export * from './asyncForEach'
|
||||||
export * from './getLintConfig'
|
export * from './getLintConfig'
|
||||||
export * from './getProjectRoot'
|
export * from './getProjectRoot'
|
||||||
|
export * from './isIgnored'
|
||||||
export * from './listSasFiles'
|
export * from './listSasFiles'
|
||||||
export * from './splitText'
|
export * from './splitText'
|
||||||
|
|||||||
119
src/utils/isIgnored.spec.ts
Normal file
119
src/utils/isIgnored.spec.ts
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import * as fileModule from '@sasjs/utils/file'
|
||||||
|
import * as getLintConfigModule from './getLintConfig'
|
||||||
|
import { getProjectRoot, DefaultLintConfiguration, isIgnored } from '.'
|
||||||
|
import { LintConfig } from '../types'
|
||||||
|
|
||||||
|
describe('isIgnored', () => {
|
||||||
|
it('should return true if provided path matches the patterns from .gitignore', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(getLintConfigModule, 'getLintConfig')
|
||||||
|
.mockImplementationOnce(
|
||||||
|
async () => new LintConfig(DefaultLintConfiguration)
|
||||||
|
)
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'fileExists')
|
||||||
|
.mockImplementationOnce(async () => true)
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'readFile')
|
||||||
|
.mockImplementationOnce(async () => 'sasjs')
|
||||||
|
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, 'sasjs')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(pathToTest)
|
||||||
|
|
||||||
|
expect(ignored).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true if top level path of provided path is in .gitignore', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(getLintConfigModule, 'getLintConfig')
|
||||||
|
.mockImplementationOnce(
|
||||||
|
async () => new LintConfig(DefaultLintConfiguration)
|
||||||
|
)
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'fileExists')
|
||||||
|
.mockImplementationOnce(async () => true)
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'readFile')
|
||||||
|
.mockImplementationOnce(async () => 'sasjs/common')
|
||||||
|
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, 'sasjs/common/init/init.sas')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(pathToTest)
|
||||||
|
|
||||||
|
expect(ignored).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true if provided path matches any pattern from ignoreList (.sasjslint)', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'fileExists')
|
||||||
|
.mockImplementationOnce(async () => false)
|
||||||
|
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, 'sasjs')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(
|
||||||
|
pathToTest,
|
||||||
|
new LintConfig({
|
||||||
|
...DefaultLintConfiguration,
|
||||||
|
ignoreList: ['sasjs']
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ignored).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true if top level path of provided path is in ignoreList (.sasjslint)', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'fileExists')
|
||||||
|
.mockImplementationOnce(async () => false)
|
||||||
|
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, 'sasjs/common/init/init.sas')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(
|
||||||
|
pathToTest,
|
||||||
|
new LintConfig({
|
||||||
|
...DefaultLintConfiguration,
|
||||||
|
ignoreList: ['sasjs']
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ignored).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false if provided path does not matches any pattern from .gitignore and ignoreList (.sasjslint)', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(fileModule, 'fileExists')
|
||||||
|
.mockImplementationOnce(async () => true)
|
||||||
|
|
||||||
|
jest.spyOn(fileModule, 'readFile').mockImplementationOnce(async () => '')
|
||||||
|
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, 'sasjs')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(
|
||||||
|
pathToTest,
|
||||||
|
new LintConfig(DefaultLintConfiguration)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ignored).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false if provided path is equal to projectRoot', async () => {
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const pathToTest = path.join(projectRoot, '')
|
||||||
|
|
||||||
|
const ignored = await isIgnored(
|
||||||
|
pathToTest,
|
||||||
|
new LintConfig(DefaultLintConfiguration)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ignored).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
34
src/utils/isIgnored.ts
Normal file
34
src/utils/isIgnored.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { fileExists, readFile } from '@sasjs/utils'
|
||||||
|
import path from 'path'
|
||||||
|
import ignore from 'ignore'
|
||||||
|
import { getLintConfig, getProjectRoot } from '.'
|
||||||
|
import { LintConfig } from '../types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to check if file/folder path matches any pattern from .gitignore or ignoreList (.sasjsLint)
|
||||||
|
*
|
||||||
|
* @param {string} fPath - absolute path of file or folder
|
||||||
|
* @returns {Promise<boolean>} true if path matches the patterns from .gitignore file otherwise false
|
||||||
|
*/
|
||||||
|
export const isIgnored = async (
|
||||||
|
fPath: string,
|
||||||
|
configuration?: LintConfig
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const config = configuration || (await getLintConfig())
|
||||||
|
const projectRoot = await getProjectRoot()
|
||||||
|
const gitIgnoreFilePath = path.join(projectRoot, '.gitignore')
|
||||||
|
const rootPath = projectRoot + path.sep
|
||||||
|
const relativePath = fPath.replace(rootPath, '')
|
||||||
|
|
||||||
|
if (fPath === projectRoot) return false
|
||||||
|
|
||||||
|
let gitIgnoreFileContent = ''
|
||||||
|
|
||||||
|
if (await fileExists(gitIgnoreFilePath))
|
||||||
|
gitIgnoreFileContent = await readFile(gitIgnoreFilePath)
|
||||||
|
|
||||||
|
return ignore()
|
||||||
|
.add(gitIgnoreFileContent)
|
||||||
|
.add(config.ignoreList)
|
||||||
|
.ignores(relativePath)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user