Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spec): eslint with custom rules APIC-387 #304

Merged
merged 10 commits into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
{
files: ['*.yml'],
parser: 'yaml-eslint-parser',
plugins: ["automation-custom"],
rules: {
'@typescript-eslint/naming-convention': 0,
'yml/quotes': [
Expand All @@ -31,10 +32,14 @@ module.exports = {
},
],
'yml/require-string-key': 2,

// Should be removed once the specs are finished
'yml/no-empty-document': 0,
},
overrides: [{
files: ['specs/**/*.yml'],
rules: {
"automation-custom/description-dot": "error",
}
}
]
},
],

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ jobs:
- name: Test scripts
run: yarn scripts:test

- name: Test custom eslint plugin
run: yarn workspace eslint-plugin-automation-custom test

specs:
runs-on: ubuntu-20.04
timeout-minutes: 10
Expand Down
8 changes: 8 additions & 0 deletions eslint/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Config } from '@jest/types';

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
};

export default config;
22 changes: 22 additions & 0 deletions eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "eslint-plugin-automation-custom",
"version": "1.0.0",
"description": "Custom rules for eslint",
"packageManager": "[email protected]",
"main": "dist/index.js",
"files": [
"src/**.ts"
],
"scripts": {
"build": "tsc",
"test": "jest"
},
"devDependencies": {
"@types/jest": "27.4.1",
"eslint": "8.11.0",
"jest": "27.5.1",
"ts-jest": "27.1.3",
"ts-node": "10.7.0",
"typescript": "4.5.4"
}
}
7 changes: 7 additions & 0 deletions eslint/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { descriptionDot } from './rules/descriptionDot';

const rules = {
'description-dot': descriptionDot,
};

export { rules };
60 changes: 60 additions & 0 deletions eslint/src/rules/descriptionDot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-disable no-console */
import type { Rule } from 'eslint';

import { isBLockScalar, isPairWithKey, isScalar } from '../utils';

export const descriptionDot: Rule.RuleModule = {
meta: {
docs: {
description: 'description must end with a dot',
},
messages: {
descriptionNoDot: 'description does not end with a dot',
},
fixable: 'code',
},
create(context) {
if (!context.parserServices.isYAML) {
return {};
}

return {
YAMLPair(node): void {
if (!isPairWithKey(node, 'description')) {
return;
}
if (!isScalar(node.value)) {
return;
}
const value = node.value;
if (
typeof value.value !== 'string' ||
value.value.trim().endsWith('.') ||
!value.value.trim().includes(' ')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it mean?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment for clarity

) {
// The rule is respected if:
// the description is not a string
// or it ends with a dot
// or it's a single word (like 'OK' or 'Success', it's not a sentence)
return;
}

// trim the whitespaces at the end before adding the dot. This assume the indent is 2
let toTrim = value.value.length - value.value.trimEnd().length;
if (isBLockScalar(value)) {
toTrim += node.key!.loc.start.column + 2;
}
context.report({
node: node as any,
messageId: 'descriptionNoDot',
fix(fixer) {
return fixer.insertTextAfterRange(
[0, value.range[1] - toTrim],
'.'
);
},
});
},
};
},
};
20 changes: 20 additions & 0 deletions eslint/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { AST } from 'yaml-eslint-parser';

export function isScalar(node: AST.YAMLNode | null): node is AST.YAMLScalar {
return node !== null && node.type === 'YAMLScalar';
}

export function isBLockScalar(
node: AST.YAMLNode | null
): node is AST.YAMLBlockFoldedScalar | AST.YAMLBlockLiteralScalar {
return isScalar(node) && 'chomping' in node;
}

export function isPairWithKey(
node: AST.YAMLNode | null,
key: string
): node is AST.YAMLPair {
if (node === null || node.type !== 'YAMLPair' || node.key === null)
return false;
return isScalar(node.key) && node.key.value === key;
}
63 changes: 63 additions & 0 deletions eslint/tests/descriptionDot.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { RuleTester } from 'eslint';

import { descriptionDot } from '../src/rules/descriptionDot';

const ruleTester = new RuleTester({
parser: require.resolve('yaml-eslint-parser'),
});

ruleTester.run('description-dot', descriptionDot, {
valid: [
`
simple:
type: number
description: a number.
`,
`
multi:
description: >
Creates a new A/B test with provided configuration.

You can set an A/B test on two different indices with different settings, or on the same index with different search parameters by providing a customSearchParameters setting on one of the variants.
`,
`
multiStrip:
description: >-
Multiline comment
on description.
`,
`
responses:
'200':
description: OK
`,
],
invalid: [
{
code: `
simple:
description: a number
`,
errors: [{ messageId: 'descriptionNoDot' }],
output: `
simple:
description: a number.
`,
},
{
code: `
multi:
description: >
Multiline comment
on description
`,
errors: [{ messageId: 'descriptionNoDot' }],
output: `
multi:
description: >
Multiline comment
on description.
`,
},
],
});
13 changes: 13 additions & 0 deletions eslint/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../config/base.tsconfig.json",
"compilerOptions": {
"outDir": "dist",
},
"include": [
"src/**/*.ts",
],
"exclude": [
"node_modules",
"dist"
]
}
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"playground/javascript/browser/",
"scripts/",
"tests/output/javascript",
"website/"
"website/",
"eslint"
],
"scripts": {
"cli": "yarn workspace scripts ts-node --transpile-only ./index.ts",
Expand All @@ -19,6 +20,8 @@
"docker": "docker exec -it dev yarn cli $*",
"github-actions:lint": "eslint --ext=yml .github/",
"playground:browser": "yarn workspace javascript-browser-playground start",
"postinstall": "yarn workspace eslint-plugin-automation-custom build",
"build:eslint": "yarn workspace eslint-plugin-automation-custom build && yarn install",
"release": "yarn workspace scripts createReleaseIssue",
"scripts:lint": "eslint --ext=ts scripts/",
"scripts:test": "yarn workspace scripts test",
Expand All @@ -37,6 +40,7 @@
"eslint-config-algolia": "20.0.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-algolia": "2.0.0",
"eslint-plugin-automation-custom": "1.0.0",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-import": "2.25.4",
"eslint-plugin-jsdoc": "37.9.7",
Expand Down
2 changes: 1 addition & 1 deletion specs/abtesting/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ security:
apiKey: []
tags:
- name: abtesting
description: abtesting API reference
description: abtesting API reference.
paths:
# ######################
# ### Custom request ###
Expand Down
2 changes: 1 addition & 1 deletion specs/analytics/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ security:
apiKey: []
tags:
- name: analytics
description: Analytics API reference
description: Analytics API reference.
paths:
# ######################
# ### Custom request ###
Expand Down
2 changes: 1 addition & 1 deletion specs/bundled/abtesting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ security:
apiKey: []
tags:
- name: abtesting
description: abtesting API reference
description: abtesting API reference.
paths:
/1{path}:
get:
Expand Down
2 changes: 1 addition & 1 deletion specs/bundled/analytics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ security:
apiKey: []
tags:
- name: analytics
description: Analytics API reference
description: Analytics API reference.
paths:
/1{path}:
get:
Expand Down
2 changes: 1 addition & 1 deletion specs/bundled/insights.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ security:
apiKey: []
tags:
- name: insights
description: Insights API reference
description: Insights API reference.
paths:
/1{path}:
get:
Expand Down
4 changes: 2 additions & 2 deletions specs/bundled/personalization.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ security:
apiKey: []
tags:
- name: personalization
description: Personalization API reference
description: Personalization API reference.
paths:
/1{path}:
get:
Expand Down Expand Up @@ -297,7 +297,7 @@ paths:
$ref: '#/components/schemas/userToken'
lastEventAt:
type: string
description: Date of last event update. (ISO-8601 format)
description: Date of last event update. (ISO-8601 format).
scores:
type: object
description: The userToken scores.
Expand Down
Loading