Commit 2cd307be authored by Frank Bessou's avatar Frank Bessou 💭
Browse files

feat: add a @rql/eslint-plugin package

It includes a simple implementation of a no-undeclared-var rule.
parent 3205adea99fe
Pipeline #16934 passed with stages
in 1 minute and 39 seconds
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "prettier"],
plugins: ["@rql", "@typescript-eslint", "prettier"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
......@@ -13,6 +13,7 @@ module.exports = {
},
rules: {
"no-constant-condition": ["error", { checkLoops: false }],
"@rql/no-undeclared-var": ["error"],
},
overrides: [
{
......
image: node:10-buster-slim
stages:
- lint
- test
- build
- dependencies
- build-parser
- build-eslint-plugin
- qa
lint:
stage: lint
before_script:
dependencies:
stage: dependencies
script:
- yarn
artifacts:
paths:
- ./node_modules
- ./packages/*/node_modules
build-parser:
stage: build-parser
script:
- cd packages/parser/
- yarn build
artifacts:
paths:
- ./packages/parser/build
build-eslint-plugin:
stage: build-eslint-plugin
script:
- cd packages/eslint-plugin/
- yarn build
artifacts:
paths:
- ./packages/eslint-plugin/build
lint:
stage: qa
script:
- yarn lint
test:
stage: test
before_script:
- yarn
stage: qa
script:
- yarn test
build:
stage: build
before_script:
- yarn
script:
- cd packages/parser/
- yarn build
{
"private": true,
"workspaces": [
"packages/parser"
"packages/parser",
"packages/eslint-plugin"
],
"scripts": {
"test": "jest",
......
# eslint-plugin-rql
Eslint plugin which provides lint rules for *RQL* queries
## Installation
You'll first need to install [ESLint](http://eslint.org):
```
$ npm i eslint --save-dev
```
Next, install `@rql/eslint-plugin`:
```
$ npm install @rql/eslint-plugin --save-dev
```
## Usage
Add `rql` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
```json
{
"plugins": [
"rql"
]
}
```
Then configure the rules you want to use under the rules section.
```json
{
"rules": {
"rql/rule-name": 2
}
}
```
## Supported Rules
**TO COMPLETE**
import rule from "../../../lib/rules/no-undeclared-var";
import { RuleTester } from "eslint";
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
ruleTester.run("no-undeclared-var", rule, {
valid: [
"const query = rql`Any X WHERE X is Person`",
"const query = rql`Any X, N WHERE X is Person, X name N`",
],
invalid: [
{
code: "const a = rql`Any X WHERE Y is Person`",
errors: [
{
message:
"X is declared in selection but doesn't appear in restrictions",
type: "TemplateLiteral",
},
],
},
{
code: "const a = rql`Any X, Y, Z WHERE X is Person, X knows Y`",
errors: [
{
message:
"Z is declared in selection but doesn't appear in restrictions",
type: "TemplateLiteral",
},
],
},
],
});
# Require selected variables to appear in the restriction part of search queries. (no-undeclared-var)
A RQL search query selecting a variable which is not declared in its restriction part is not valid. This ensure that all selected variables appear in restrictions.
## Rule Details
This rule aims to ensure that variables appearing in selection also appear in restrictions.
Examples of **incorrect** code for this rule:
```js
const query = rql`Any X WHERE Y is Person`
```
```js
const a = rql`Any X, Y, Z WHERE X is Person, X knows Y`
```
Examples of **correct** code for this rule:
```js
const query = rql`Any X WHERE X is Person`;
```
```js
const query = rql`Any X, N WHERE X is Person, X name N`;
```
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
import noUndeclaredVar from "./rules/no-undeclared-var";
export const rules = {
"no-undeclared-var": noUndeclaredVar,
};
import { TaggedTemplateExpression } from "estree";
import { Rule } from "eslint";
import { parse, SearchQuery, NodeKinds } from "@rql/parser";
const rule: Rule.RuleModule = {
meta: {
docs: {
description:
"Check if each variable appears in the restriction part of search queries.",
category: "possible-errors",
recommended: true,
},
messages: {
undeclaredVariable:
"{{ variableName }} is declared in selection but doesn't appear in restrictions",
},
schema: [],
},
create: function (context) {
// variables should be defined here
function selectedVariables(ast: SearchQuery) {
return ast.selection
.filter((node) => node.kind === NodeKinds.Variable)
.map((node) => node.identifier.name);
}
function restrictedVariables(ast: SearchQuery) {
const variablesInRestrictions = [];
for (const restriction of ast.restrictions) {
if (restriction.subject.kind === NodeKinds.Variable) {
variablesInRestrictions.push(restriction.subject.identifier.name);
}
if (restriction.object.kind === NodeKinds.Variable) {
variablesInRestrictions.push(restriction.object.identifier.name);
}
}
return variablesInRestrictions;
}
return {
TaggedTemplateExpression(node: TaggedTemplateExpression) {
if (node.tag.type === "Identifier" && node.tag.name === "rql") {
if (node.quasi.expressions.length > 0) {
// Ignore rql tagged template containing js expressions
return;
}
const rql = node.quasi.quasis[0].value.raw;
const ast = parse(rql);
if (ast.kind !== NodeKinds.SearchQuery) {
return;
}
const variablesInRestrictions = restrictedVariables(ast);
for (const selectedVariable of selectedVariables(ast)) {
if (!variablesInRestrictions.includes(selectedVariable)) {
context.report({
node: node.quasi,
messageId: "undeclaredVariable",
data: {
variableName: selectedVariable,
},
});
}
}
}
},
// give me methods
};
},
};
export default rule;
{
"name": "@rql/eslint-plugin",
"version": "0.0.0",
"description": "Description",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "Frank Bessou",
"main": "build/index.js",
"types": "build/types.d.ts",
"dependencies": {},
"devDependencies": {
"@types/eslint": "^7.2.2"
},
"peerDependency": {
"@rql/parser": "^0.0.0"
},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"watch": "tsc --project tsconfig.build.json -w"
},
"engines": {
"node": ">=0.10.0"
},
"license": "MIT"
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "lib",
"outDir": "build/"
},
"exclude": [
"**/__tests__/**"
]
}
......@@ -271,6 +271,22 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@eslint/eslintrc@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085"
integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==
dependencies:
ajv "^6.12.4"
debug "^4.1.1"
espree "^7.3.0"
globals "^12.1.0"
ignore "^4.0.6"
import-fresh "^3.2.1"
js-yaml "^3.13.1"
lodash "^4.17.19"
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
......@@ -525,6 +541,19 @@
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
"@types/eslint@^7.2.2":
version "7.2.3"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.3.tgz#a3731b7584fe1a847a34e67ac57a556afd9b0c0e"
integrity sha512-SPBkpC+awgFfyAn4sjt0JBZ3vzACoSp2zhGBJkkrs09EzPqLbxkzaE8kJs3EsRRgkZwWk9zyXT/swvhnJYX8pQ==
dependencies:
"@types/estree" "*"
"@types/json-schema" "*"
"@types/estree@*":
version "0.0.45"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
"@types/graceful-fs@^4.1.2":
version "4.1.3"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f"
......@@ -567,6 +596,11 @@
jest-diff "^25.2.1"
pretty-format "^25.2.1"
"@types/json-schema@*":
version "7.0.6"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
"@types/json-schema@^7.0.3":
version "7.0.5"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
......@@ -701,9 +735,9 @@ acorn-globals@^6.0.0:
acorn-walk "^7.1.1"
acorn-jsx@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
version "5.3.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
acorn-walk@^7.1.1:
version "7.2.0"
......@@ -715,7 +749,17 @@ acorn@^7.1.1, acorn@^7.4.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c"
integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3:
ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4:
version "6.12.5"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da"
integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^6.12.3:
version "6.12.4"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==
......@@ -1394,7 +1438,7 @@ eslint-plugin-prettier@^3.1.4:
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-scope@^5.0.0, eslint-scope@^5.1.0:
eslint-scope@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5"
integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==
......@@ -1402,6 +1446,14 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint-scope@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
dependencies:
esrecurse "^4.3.0"
estraverse "^4.1.1"
eslint-utils@^2.0.0, eslint-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
......@@ -1415,11 +1467,12 @@ eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint@^7.7.0:
version "7.7.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073"
integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==
version "7.9.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337"
integrity sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==
dependencies:
"@babel/code-frame" "^7.0.0"
"@eslint/eslintrc" "^0.1.3"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
......@@ -1429,7 +1482,7 @@ eslint@^7.7.0:
eslint-scope "^5.1.0"
eslint-utils "^2.1.0"
eslint-visitor-keys "^1.3.0"
espree "^7.2.0"
espree "^7.3.0"
esquery "^1.2.0"
esutils "^2.0.2"
file-entry-cache "^5.0.1"
......@@ -1456,7 +1509,7 @@ eslint@^7.7.0:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
espree@^7.2.0:
espree@^7.3.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348"
integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==
......@@ -1477,19 +1530,19 @@ esquery@^1.2.0:
dependencies:
estraverse "^5.1.0"
esrecurse@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
esrecurse@^4.1.0, esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
dependencies:
estraverse "^4.1.0"
estraverse "^5.2.0"
estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
estraverse@^4.1.1, estraverse@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
estraverse@^5.1.0:
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
......@@ -1904,7 +1957,7 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
import-fresh@^3.0.0:
import-fresh@^3.0.0, import-fresh@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==
......@@ -3675,7 +3728,7 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-json-comments@^3.1.0:
strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
......@@ -3687,13 +3740,20 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
supports-color@^7.0.0, supports-color@^7.1.0:
supports-color@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
dependencies:
has-flag "^4.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
supports-hyperlinks@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47"
......@@ -3916,9 +3976,9 @@ unset-value@^1.0.0:
isobject "^3.0.0"
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
version "4.4.0"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
dependencies:
punycode "^2.1.0"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment