Skip to content

Commit

Permalink
chore: lint JSON/YAML/Markdown files (#1360)
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed Oct 5, 2020
1 parent c2fe4a7 commit 28ecf19
Show file tree
Hide file tree
Showing 24 changed files with 464 additions and 820 deletions.
1 change: 1 addition & 0 deletions .eslintignore
@@ -1 +1,2 @@
**/__fixtures__/**
/test-harness/**/*.yaml
12 changes: 11 additions & 1 deletion .prettierrc.json
Expand Up @@ -4,5 +4,15 @@
"trailingComma": "all",
"printWidth": 120,
"proseWrap": "always",
"endOfLine": "auto"
"endOfLine": "auto",
"overrides": [
{
"files": ["*.md"],
"options": {
"singleQuote": false,
"printWidth": 200,
"proseWrap": "preserve"
}
}
]
}
4 changes: 1 addition & 3 deletions README.md
@@ -1,8 +1,6 @@
![Spectral logo](./docs/img/spectral-banner.png)

[![CircleCI](https://img.shields.io/circleci/build/github/stoplightio/spectral/master)](https://circleci.com/gh/stoplightio/spectral)
[![NPM Downloads](https://img.shields.io/npm/dw/@stoplight/spectral?color=blue)](https://www.npmjs.com/package/@stoplight/spectral)
[![Treeware (Trees)](https://img.shields.io/treeware/trees/stoplightio/spectral)](https://plant.treeware.earth/stoplightio/spectral)
[![CircleCI](https://img.shields.io/circleci/build/github/stoplightio/spectral/master)](https://circleci.com/gh/stoplightio/spectral) [![NPM Downloads](https://img.shields.io/npm/dw/@stoplight/spectral?color=blue)](https://www.npmjs.com/package/@stoplight/spectral) [![Treeware (Trees)](https://img.shields.io/treeware/trees/stoplightio/spectral)](https://plant.treeware.earth/stoplightio/spectral)

A flexible JSON/YAML linter, with out of the box support for OpenAPI v2/v3 and AsyncAPI v2.

Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/2-installation.md
Expand Up @@ -48,7 +48,7 @@ validate_open-api:
name: stoplight/spectral
entrypoint: [""]
script:
- spectral lint file.yaml
- spectral lint file.yaml
```

For more details about `entrypoint: [""]` see this issue on GitLab [here](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2692#note_50147081)
For more details about `entrypoint: [""]` see this issue on GitLab [here](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2692#note_50147081)
6 changes: 3 additions & 3 deletions docs/getting-started/3-rulesets.md
Expand Up @@ -41,9 +41,9 @@ Extends can reference any [distributed ruleset](../guides/7-sharing-rulesets.md)

```yaml
extends:
- ./config/spectral.json
- https://example.org/api/style.yaml
- some-npm-module
- ./config/spectral.json
- https://example.org/api/style.yaml
- some-npm-module
```

The `extends` keyword can be combined with extra rules in order to extend and override rulesets. Learn more about that in [custom rulesets](../guides/4-custom-rulesets.md).
Expand Down
1 change: 1 addition & 0 deletions docs/getting-started/4-openapi.md
Expand Up @@ -5,4 +5,5 @@ Spectral is a generic linter, but a lot of effort has been put in to making sure
Run Spectral against a document without specifying a ruleset will trigger an auto-detect, where Spectral will look to see if `swagger: 2.0` or `openapi: 3.0.x` are in the root of the document. If it finds either of those it will load `spectral:oas`, which is documented in our [Reference > OpenAPI Rules](../reference/openapi-rules.md).

<!-- theme: info -->

> If you would like support for other API description formats like [RAML](https://raml.org/), message formats like [JSON:API](https://jsonapi.org/), etc., we recommend you start building custom but generic rulesets which can be shared with others. We've started putting together some over here on [OpenAPI Contrib](https://github.com/openapi-contrib/style-guides/).
8 changes: 4 additions & 4 deletions docs/guides/2-cli.md
Expand Up @@ -64,21 +64,21 @@ To have requests made from Spectral be proxied through a server, you'd need to s

`PROXY=<<PROXY_SERVER_ADDRESS>> spectral lint spec.yaml`

## Custom $ref Resolving
## Custom \$ref Resolving

If you want to customize $ref resolving, you can leverage `--resolver` flag and pass a path to the JS file exporting a custom instance of json-ref-resolver Resolver.
If you want to customize \$ref resolving, you can leverage `--resolver` flag and pass a path to the JS file exporting a custom instance of json-ref-resolver Resolver.

### Example

Assuming the filename is called `my-resolver.js` and the content looks as follows, the path should look more or less like `--resolver=./my-resolver.js`.

```js
const { Resolver } = require('@stoplight/json-ref-resolver');
const { Resolver } = require("@stoplight/json-ref-resolver");

module.exports = new Resolver({
resolvers: {
// pass any resolver for protocol you need
}
},
});
```

Expand Down
149 changes: 70 additions & 79 deletions docs/guides/3-javascript.md
Expand Up @@ -7,37 +7,38 @@ Assuming it has been installed as a Node module via NPM/Yarn, it can be used to
## Linting a YAML String

```js
const { Spectral, Document, Parsers } = require('@stoplight/spectral');
const { Spectral, Document, Parsers } = require("@stoplight/spectral");

const myOpenApiDocument = new Document(`responses:
const myOpenApiDocument = new Document(
`responses:
'200':
description: ''
schema:
$ref: '#/definitions/error-response'
`, Parsers.Yaml);
`,
Parsers.Yaml,
);

const spectral = new Spectral();
spectral
.run(myOpenApiDocument)
.then(console.log);
spectral.run(myOpenApiDocument).then(console.log);
```

This will run Spectral with no formats, rules or functions, so it's not going to do anything besides $ref resolving.
This will run Spectral with no formats, rules or functions, so it's not going to do anything besides \$ref resolving.
Find out how to add formats, rules and functions below.

## Linting an Object

Instead of passing a string to `Document`, you can pass in JavaScript object, with or without `$ref`'s.

```js
const { Spectral } = require('@stoplight/spectral');
const { Spectral } = require("@stoplight/spectral");

const myOpenApiDocument = {
responses: {
'200': {
description: '',
"200": {
description: "",
schema: {
$ref: '#/definitions/error-response',
$ref: "#/definitions/error-response",
},
},
},
Expand All @@ -57,33 +58,25 @@ Assuming your rulesets use the built-in Spectral formats, this can be accomplish
- OpenAPI

```js
const { Spectral, isOpenApiv2, isOpenApiv3 } = require('@stoplight/spectral');
const { Spectral, isOpenApiv2, isOpenApiv3 } = require("@stoplight/spectral");

const spectral = new Spectral();
spectral.registerFormat('oas2', isOpenApiv2);
spectral.registerFormat('oas3', isOpenApiv3);
spectral.registerFormat("oas2", isOpenApiv2);
spectral.registerFormat("oas3", isOpenApiv3);
```

- JSON Schema

```js
const {
Spectral,
isJSONSchema,
isJSONSchemaDraft4,
isJSONSchemaDraft6,
isJSONSchemaDraft7,
isJSONSchemaDraft2019_09,
isJSONSchemaLoose,
} = require('@stoplight/spectral');
const { Spectral, isJSONSchema, isJSONSchemaDraft4, isJSONSchemaDraft6, isJSONSchemaDraft7, isJSONSchemaDraft2019_09, isJSONSchemaLoose } = require("@stoplight/spectral");

const spectral = new Spectral();
spectral.registerFormat('json-schema', isJSONSchema);
spectral.registerFormat('json-schema-loose', isJSONSchemaLoose);
spectral.registerFormat('json-schema-draft4', isJSONSchemaDraft4);
spectral.registerFormat('json-schema-draft6', isJSONSchemaDraft6);
spectral.registerFormat('json-schema-draft7', isJSONSchemaDraft7);
spectral.registerFormat('json-schema-2019-09', isJSONSchemaDraft2019_09);
spectral.registerFormat("json-schema", isJSONSchema);
spectral.registerFormat("json-schema-loose", isJSONSchemaLoose);
spectral.registerFormat("json-schema-draft4", isJSONSchemaDraft4);
spectral.registerFormat("json-schema-draft6", isJSONSchemaDraft6);
spectral.registerFormat("json-schema-draft7", isJSONSchemaDraft7);
spectral.registerFormat("json-schema-2019-09", isJSONSchemaDraft2019_09);
```

Learn more about predefined formats in the [ruleset documentation](../getting-started/3-rulesets.md#formats).
Expand All @@ -93,20 +86,21 @@ Learn more about predefined formats in the [ruleset documentation](../getting-st
Spectral comes with some rulesets that are very specific to OpenAPI v2/v3, and they can be loaded using `Spectral.loadRuleset()`.

```js
const { Spectral, isOpenApiv2, isOpenApiv3 } = require('@stoplight/spectral');
const { Spectral, isOpenApiv2, isOpenApiv3 } = require("@stoplight/spectral");

const myOpenApiDocument = `
openapi: 3.0.0
# here goes the rest of document
`
`;

const spectral = new Spectral();
spectral.registerFormat('oas2', isOpenApiv2);
spectral.registerFormat('oas3', isOpenApiv3);
spectral.loadRuleset('spectral:oas')
spectral.registerFormat("oas2", isOpenApiv2);
spectral.registerFormat("oas3", isOpenApiv3);
spectral
.loadRuleset("spectral:oas")
.then(() => spectral.run(myOpenApiDocument))
.then(results => {
console.log('here are the results', results);
console.log("here are the results", results);
});
```

Expand Down Expand Up @@ -137,15 +131,14 @@ spectral.loadRuleset(join(__dirname './path/to/my-ruleset.yaml'));
Alternatively, if your ruleset is stored in a plain JSON file that doesn't extend any other rulesets, you can also consider using `setRuleset`, as follows

```js
const { Spectral } = require('@stoplight/spectral');
const ruleset = require('./my-ruleset.json');
const { Spectral } = require("@stoplight/spectral");
const ruleset = require("./my-ruleset.json");

const spectral = new Spectral();
spectral.setRuleset(ruleset);
spectral.run(myOpenApiDocument)
.then(results => {
console.log('here are the results', results);
});
spectral.run(myOpenApiDocument).then(results => {
console.log("here are the results", results);
});
```

## Advanced
Expand All @@ -155,101 +148,99 @@ spectral.run(myOpenApiDocument)
Spectral supports two core formats: `oas2` and `oas3`. Using `registerFormat` you can add support for auto-detecting other formats. You might want to do this for a ruleset which is run against multiple major versions of description format like RAML v0.8 and v1.0.

```js
const { Spectral } = require('@stoplight/spectral');
const { Spectral } = require("@stoplight/spectral");

const spectral = new Spectral();

spectral.registerFormat('foo-bar', obj => typeof obj === 'object' && obj !== null && 'foo-bar' in obj);
spectral.registerFormat("foo-bar", obj => typeof obj === "object" && obj !== null && "foo-bar" in obj);

spectral.setRuleset({
functions: {},
rules: {
rule1: {
given: '$.x',
formats: ['foo-bar'],
severity: 'error',
given: "$.x",
formats: ["foo-bar"],
severity: "error",
then: {
function: 'truthy',
function: "truthy",
},
},
},
});

spectral
.run({
'foo-bar': true,
x: false
"foo-bar": true,
x: false,
})
.then(result => {
expect(result).toEqual([
expect.objectContaining({
code: 'rule1',
}),
]);
code: "rule1",
}),
]);
});
```

Alternatively you may lookup for certain format by optional `source`, which could be passed in `run` options.

```js
const { Document, Spectral } = require('@stoplight/spectral');
const { Document, Spectral } = require("@stoplight/spectral");

const spectral = new Spectral();

spectral.registerFormat('foo-bar', (_, source) => source === '/foo/bar');
spectral.registerFormat("foo-bar", (_, source) => source === "/foo/bar");

spectral.setRuleset({
functions: {},
rules: {
rule1: {
given: '$.x',
formats: ['foo-bar'],
severity: 'error',
given: "$.x",
formats: ["foo-bar"],
severity: "error",
then: {
function: 'truthy',
function: "truthy",
},
},
},
});

spectral
.run(new Document(`foo-bar: true\nx: false`, Parsers.Yaml, '/foo/bar'))
.then(result => {
expect(result).toEqual([
expect.objectContaining({
code: 'rule1',
}),
]);
});
spectral.run(new Document(`foo-bar: true\nx: false`, Parsers.Yaml, "/foo/bar")).then(result => {
expect(result).toEqual([
expect.objectContaining({
code: "rule1",
}),
]);
});
```

### Using a Proxy

Spectral supports HTTP(S) proxies when fetching remote schemas and rulesets.

```js
const { Spectral } = require('@stoplight/spectral');
const { Spectral } = require("@stoplight/spectral");

const spectral = new Spectral({ proxyUri: 'http://my-proxy:3000' });
spectral.loadRuleset('https://example.org/my-rules')
const spectral = new Spectral({ proxyUri: "http://my-proxy:3000" });
spectral.loadRuleset("https://example.org/my-rules");

// lint as usual - $refs and rules will be requested using the proxy
```

### Using a Custom Resolver

Spectral lets you provide any custom $ref resolver. By default, http(s) and file protocols are resolved, relatively to
Spectral lets you provide any custom \$ref resolver. By default, http(s) and file protocols are resolved, relatively to
the document Spectral lints against. If you'd like support any additional protocol or adjust the resolution, you are
absolutely fine to do it. In order to achieve that, you need to create a custom json-ref-resolver instance.

You can find more information about how to create custom resolvers in
the [@stoplight/json-ref-resolver](https://github.com/stoplightio/json-ref-resolver) repository.

```js
const path = require('path');
const fs = require('fs');
const { Spectral } = require('@stoplight/spectral');
const { Resolver } = require('@stoplight/json-ref-resolver');
const path = require("path");
const fs = require("fs");
const { Spectral } = require("@stoplight/spectral");
const { Resolver } = require("@stoplight/json-ref-resolver");

const customFileResolver = new Resolver({
resolvers: {
Expand All @@ -258,17 +249,17 @@ const customFileResolver = new Resolver({
return new Promise((resolve, reject) => {
const basePath = process.cwd();
const refPath = ref.path();
fs.readFile(path.join(basePath, refPath), 'utf8', (err, data) => {
fs.readFile(path.join(basePath, refPath), "utf8", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
}
}
},
},
},
});

const spectral = new Spectral({ resolver: customFileResolver });
Expand Down

0 comments on commit 28ecf19

Please sign in to comment.