Skip to content

test(eslint-plugin): assert that ts/tsx code blocks in docs are syntactically valid #8142

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

Merged
merged 14 commits into from
Jan 29, 2024
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
4 changes: 2 additions & 2 deletions packages/eslint-plugin/docs/rules/ban-tslint-comment.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Useful when migrating from TSLint to ESLint. Once TSLint has been removed, this

### ❌ Incorrect

```js
```ts
/* tslint:disable */
/* tslint:enable */
/* tslint:disable:rule1 rule2 rule3... */
Expand All @@ -28,7 +28,7 @@ someCode(); // tslint:disable-line

### ✅ Correct

```js
```ts
// This is a comment that just happens to mention tslint
/* This is a multiline comment that just happens to mention tslint */
someCode(); // This is a comment that just happens to mention tslint
Expand Down
38 changes: 20 additions & 18 deletions packages/eslint-plugin/docs/rules/consistent-type-assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,22 @@ Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'neve
#### ❌ Incorrect

```ts option='{ "assertionStyle": "as", "objectLiteralTypeAssertions": "never" }'
const x = { ... } as T;
const x = { foo: 1 } as T;

function foo() {
return { ... } as T;
function bar() {
return { foo: 1 } as T;
}
```

#### ✅ Correct

```ts option='{ "assertionStyle": "as", "objectLiteralTypeAssertions": "never" }'
const x: T = { ... };
const y = { ... } as any;
const z = { ... } as unknown;
const x: T = { foo: 1 };
const y = { foo: 1 } as any;
const z = { foo: 1 } as unknown;

function foo(): T {
return { ... };
function bar(): T {
return { foo: 1 };
}
```

Expand All @@ -82,23 +82,25 @@ Examples of code for `{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allo
#### ❌ Incorrect

```ts option='{ "assertionStyle": "as", "objectLiteralTypeAssertions": "allow-as-parameter" }'
const x = { ... } as T;
const x = { foo: 1 } as T;

function foo() {
return { ... } as T;
function bar() {
return { foo: 1 } as T;
}
```

#### ✅ Correct

```tsx option='{ "assertionStyle": "as", "objectLiteralTypeAssertions": "allow-as-parameter" }'
const x: T = { ... };
const y = { ... } as any;
const z = { ... } as unknown;
foo({ ... } as T);
new Clazz({ ... } as T);
function foo() { throw { bar: 5 } as Foo }
const foo = <Foo props={{ ... } as Bar}/>;
const x: T = { foo: 1 };
const y = { foo: 1 } as any;
const z = { foo: 1 } as unknown;
bar({ foo: 1 } as T);
new Clazz({ foo: 1 } as T);
function bar() {
throw { foo: 1 } as Foo;
}
const foo = <Foo props={{ bar: 1 } as Bar} />;
```

<!--/tabs-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ type FuncType = () => string;

let arrowFn: FuncType = () => 'test';

let funcExpr: FuncType = function() {
let funcExpr: FuncType = function () {
return 'test';
};

Expand All @@ -163,19 +163,15 @@ let objectPropCast = <ObjectType>{
foo: () => 1,
};

declare functionWithArg(arg: () => number);
declare function functionWithArg(arg: () => number);
functionWithArg(() => 1);

declare functionWithObjectArg(arg: { method: () => number });
declare function functionWithObjectArg(arg: { method: () => number });
functionWithObjectArg({
method() {
return 1;
},
});

const Comp: FC = () => {
return <button onClick={() => {}} />;
};
Comment on lines -175 to -178
Copy link
Member Author

Choose a reason for hiding this comment

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

We can't mix angle bracket assertions and jsx syntax

```

### `allowHigherOrderFunctions`
Expand Down Expand Up @@ -271,7 +267,7 @@ function foo<T>(t: T): T {

const bar = <T>(t: T): T => t;

const allowedFunction(x: string) {
function allowedFunction(x: string) {
return x;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ If you are working on a codebase within which you lint non-TypeScript code (i.e.

This rule in its default state requires no configuration and will enforce that every class member has an accessibility modifier. If you would like to allow for some implicit public members then you have the following options:

```ts
```jsonc
{
accessibility: 'explicit',
overrides: {
accessors: 'explicit',
constructors: 'no-public',
methods: 'explicit',
properties: 'off',
parameterProperties: 'explicit'
"accessibility: "explicit",
"overrides": {
"accessors": "explicit",
"constructors": "no-public",
"methods": "explicit",
"properties": "off",
"parameterProperties": "explicit"
}
}
```
Expand Down
2 changes: 0 additions & 2 deletions packages/eslint-plugin/docs/rules/member-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,6 @@ It ignores any member group types completely by specifying `"never"` for `member

```ts option='{ "default": { "memberTypes": "never", "order": "alphabetically" } }'
interface Foo {
static c = 0;
b(): void;
a: boolean;

Expand All @@ -936,7 +935,6 @@ interface Foo {
interface Foo {
a: boolean;
b(): void;
static c = 0;

[a: string]: number; // Order doesn't matter (no sortable identifier)
new (): Bar; // Order doesn't matter (no sortable identifier)
Expand Down
3 changes: 0 additions & 3 deletions packages/eslint-plugin/docs/rules/no-empty-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ class Foo {
Examples of correct code for the `{ "allow": ["decoratedFunctions"] }` option:

```ts option='{ "allow": ["decoratedFunctions"] }' showPlaygroundButton
@decorator()
function foo() {}

class Foo {
@decorator()
foo() {}
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin/docs/rules/no-for-in-array.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ You may have confused for-in with for-of, which iterates over the elements of th

### ❌ Incorrect

```js
```ts
declare const array: string[];

for (const i in array) {
Expand All @@ -35,7 +35,7 @@ for (const i in array) {

### ✅ Correct

```js
```ts
declare const array: string[];

for (const value of array) {
Expand All @@ -48,7 +48,7 @@ for (let i = 0; i < array.length; i += 1) {

array.forEach((value, i) => {
console.log(i, value);
})
});

for (const [i, value] of array.entries()) {
console.log(i, value);
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-plugin/docs/rules/no-this-alias.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ or not managing scope well.

### ❌ Incorrect

```js
```ts
const self = this;

setTimeout(function () {
Expand All @@ -25,7 +25,7 @@ setTimeout(function () {

### ✅ Correct

```js
```ts
setTimeout(() => {
this.doWork();
});
Expand Down
32 changes: 21 additions & 11 deletions packages/eslint-plugin/docs/rules/no-type-alias.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,42 @@ In TypeScript, type aliases serve three purposes:
```ts
// this...
type Person = {
firstName: string,
lastName: string,
age: number
firstName: string;
lastName: string;
age: number;
};

function addPerson(person : Person) { ... }
function addPerson(person: Person) {
// ...
}

// is easier to read than this...
function addPerson(person : { firstName: string, lastName: string, age: number}) { ... }
function addPerson(person: {
firstName: string;
lastName: string;
age: number;
}) {
// ...
}
```

- Act sort of like an interface, providing a set of methods and properties that must exist
in the objects implementing the type.

```ts
type Person = {
firstName: string,
lastName: string,
age: number,
walk: () => void,
talk: () => void
firstName: string;
lastName: string;
age: number;
walk: () => void;
talk: () => void;
};

// you know person will have 3 properties and 2 methods,
// because the structure has already been defined.
var person : Person = { ... }
var person: Person = {
// ...
};

// so we can be sure that this will work
person.walk();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ Type parameters in TypeScript may specify a default value.
For example:

```ts
function f<T = number>(...) {...}
function f<T = number>(/* ... */) {
// ...
}
```

It is redundant to provide an explicit type parameter equal to that default: e.g. calling `f<number>(...)`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface Foo<T> {}
type Bar<T> = {};

class Baz<T> {
qux<U> { }
qux<U>() {}
}

const Quux = <T>() => {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ enum Direction {

enum Color {
Red,
Green = 'Green'
Green = 'Green',
Blue = 'Blue',
}
```
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-plugin/docs/rules/prefer-for-of.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ This rule recommends a for-of loop when the loop index is only used to read from

### ❌ Incorrect

```js
```ts
declare const array: string[];

for (let i = 0; i < array.length; i++) {
Expand All @@ -28,7 +28,7 @@ for (let i = 0; i < array.length; i++) {

### ✅ Correct

```js
```ts
declare const array: string[];

for (const x of array) {
Expand Down
7 changes: 5 additions & 2 deletions packages/eslint-plugin/docs/rules/space-before-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ It adds support for interfaces and enums.

### ❌ Incorrect

<!-- prettier-ignore -->
```ts
enum Breakpoint{
Large, Medium;
Large,
Medium,
}

interface State{
Expand All @@ -27,7 +29,8 @@ interface State{

```ts
enum Breakpoint {
Large, Medium;
Large,
Medium,
}

interface State {
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin/docs/rules/typedef.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,13 @@ const text = 'text';

```ts option='{ "variableDeclaration": true, "variableDeclarationIgnoreFunction": true }'
const a = (): void => {};
const b = function (): void => {};
const b = function (): void {};
const c: () => void = (): void => {};

class Foo {
a = (): void => {};
b = function (): void => {};
c = () => void = (): void => {};
b = function (): void {};
c: () => void = (): void => {};
}
```

Expand Down
27 changes: 27 additions & 0 deletions packages/eslint-plugin/tests/docs.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { parseForESLint } from '@typescript-eslint/parser';
import fs from 'fs';
import { marked } from 'marked';
import path from 'path';
Expand Down Expand Up @@ -180,6 +181,32 @@ describe('Validating rule docs', () => {
}
});
}

test('must include only valid code samples', () => {
for (const token of tokens) {
if (token.type !== 'code') {
continue;
}

const lang = token.lang?.trim();
if (!lang || !/^tsx?\b/i.test(lang)) {
continue;
}

try {
parseForESLint(token.text, {
ecmaFeatures: {
jsx: /^tsx\b/i.test(lang),
},
ecmaVersion: 'latest',
sourceType: 'module',
range: true,
Copy link
Member

Choose a reason for hiding this comment

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

Why this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Without range: true parser throws Cannot read properties of undefined (reading '0') on valid code

});
} catch {
throw new Error(`Parsing error:\n\n${token.text}`);
}
}
});
});
}
});
Expand Down