mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 04:21:09 +00:00
fix: document openapi-core package usage + slightly improve the API (#1284)
* fix: document openapi-core package usage + slightly improve the API * chore: fix tests * Apply suggestions from code review * docs: test code and add some explanations for each section * fix: make the bundle output variables match in both examples * chore: add tests for lint and bundle * chore: add tests for bundleFromString and createConfig with a plugin * chore: apply prettier * Update packages/core/src/__tests__/bundle.test.ts --------- Co-authored-by: Lorna Mitchell <lorna.mitchell@redocly.com> Co-authored-by: Andrew Tatomyr <andrew.tatomyr@redocly.com>
This commit is contained in:
5
.changeset/two-shoes-sniff.md
Normal file
5
.changeset/two-shoes-sniff.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@redocly/openapi-core': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: enhance public API by small typescript typing fix and exporting new function `bundleFromString`
|
||||||
@@ -14,12 +14,12 @@ module.exports = {
|
|||||||
'packages/core/': {
|
'packages/core/': {
|
||||||
statements: 80,
|
statements: 80,
|
||||||
branches: 71,
|
branches: 71,
|
||||||
functions: 70,
|
functions: 71,
|
||||||
lines: 80,
|
lines: 80,
|
||||||
},
|
},
|
||||||
'packages/cli/': {
|
'packages/cli/': {
|
||||||
statements: 55,
|
statements: 55,
|
||||||
branches: 47,
|
branches: 48,
|
||||||
functions: 55,
|
functions: 55,
|
||||||
lines: 55,
|
lines: 55,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,24 +2,267 @@
|
|||||||
|
|
||||||
See https://github.com/Redocly/redocly-cli
|
See https://github.com/Redocly/redocly-cli
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
The `openapi-core package` is designed for our internal use; the interfaces that are considered safe to use are documented below.
|
||||||
|
Some of the function arguments are not documented below because they are not intended for public use.
|
||||||
|
Avoid using any functions or features that are not documented below.
|
||||||
|
If your use case is not documented below, please open an issue.
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
### Lint
|
### Lint from file system
|
||||||
|
|
||||||
|
[Lint](https://redocly.com/docs/cli/commands/lint/) a file, optionally with a [config file](https://redocly.com/docs/cli/configuration/).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { formatProblems, lint, loadConfig } from '@redocly/openapi-core';
|
import { lint, loadConfig } from '@redocly/openapi-core';
|
||||||
|
|
||||||
const pathToApi = 'openapi.yaml';
|
const pathToApi = 'openapi.yaml';
|
||||||
const config = loadConfig({ configPath: 'optional/path/to/.redocly.yaml' });
|
const config = await loadConfig({ configPath: 'optional/path/to/redocly.yaml' });
|
||||||
const lintResults = await lint({ ref: pathToApi, config });
|
const lintResults = await lint({ ref: pathToApi, config });
|
||||||
```
|
```
|
||||||
|
|
||||||
### Bundle
|
The content of `lintResults` describes any errors or warnings found during linting; an empty array means no problems were found.
|
||||||
|
For each problem, the rule, severity, feedback message and a location object are provided.
|
||||||
|
To learn more, [check the `lint` function section](#lint).
|
||||||
|
|
||||||
|
### Bundle from file system
|
||||||
|
|
||||||
|
[Bundle](https://redocly.com/docs/cli/commands/bundle/) an API description into a single structure, optionally with a [config file](https://redocly.com/docs/cli/configuration/).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { formatProblems, bundle, loadConfig } from '@redocly/openapi-core';
|
import { bundle, loadConfig } from '@redocly/openapi-core';
|
||||||
|
|
||||||
const pathToApi = 'openapi.yaml';
|
const pathToApi = 'openapi.yaml';
|
||||||
const config = loadConfig({ configPath: 'optional/path/to/.redocly.yaml' });
|
const config = await loadConfig({ configPath: 'optional/path/to/redocly.yaml' });
|
||||||
const { bundle, problems } = await bundle({ ref: pathToApi, config });
|
const bundleResults = await bundle({ ref: pathToApi, config });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In `bundleResults`, the `bundle.parsed` field has the bundled API description.
|
||||||
|
For more information, [check the `bundle` function section](#bundle).
|
||||||
|
|
||||||
|
### Lint from memory with config
|
||||||
|
|
||||||
|
[Lint](https://redocly.com/docs/cli/commands/lint/) an API description, with configuration defined. This is useful if the API description you're working with isn't a file on disk.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { lintFromString, createConfig, stringifyYaml } from '@redocly/openapi-core';
|
||||||
|
|
||||||
|
const config = await createConfig(
|
||||||
|
{
|
||||||
|
extends: ['minimal'],
|
||||||
|
rules: {
|
||||||
|
'operation-description': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// optionally provide config path for resolving $refs and proper error locations
|
||||||
|
configPath: 'optional/path/to/redocly.yaml',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const source = stringifyYaml({ openapi: '3.0.1' /* ... */ }); // you can also use JSON.stringify
|
||||||
|
const lintResults = await lintFromString({
|
||||||
|
source,
|
||||||
|
// optionally pass path to the file for resolving $refs and proper error locations
|
||||||
|
absoluteRef: 'optional/path/to/openapi.yaml',
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint from memory with a custom plugin
|
||||||
|
|
||||||
|
[Lint](https://redocly.com/docs/cli/commands/lint/) an API description, with configuration including a [custom plugin](https://redocly.com/docs/cli/custom-plugins/) to define a rule.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { lintFromString, createConfig, stringifyYaml } from '@redocly/openapi-core';
|
||||||
|
|
||||||
|
const CustomRule = (ruleOptions) => {
|
||||||
|
return {
|
||||||
|
Operation() {
|
||||||
|
// some rule logic
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = await createConfig({
|
||||||
|
extends: ['recommended'],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
id: 'pluginId',
|
||||||
|
rules: {
|
||||||
|
oas3: {
|
||||||
|
customRule1: CustomRule,
|
||||||
|
},
|
||||||
|
oas2: {
|
||||||
|
customRule1: CustomRule, // if the same rule can handle both oas3 and oas2
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// enable rule
|
||||||
|
rules: {
|
||||||
|
'pluginId/customRule1': 'error',
|
||||||
|
},
|
||||||
|
decorators: {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const source = stringifyYaml({ openapi: '3.0.1' /* ... */ }); // you can also use JSON.stringify
|
||||||
|
const lintResults = await lintFromString({
|
||||||
|
source,
|
||||||
|
// optionally pass path to the file for resolving $refs and proper error locations
|
||||||
|
absoluteRef: 'optional/path/to/openapi.yaml',
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bundle from memory
|
||||||
|
|
||||||
|
[Bundle](https://redocly.com/docs/cli/commands/bundle/) an API description into a single structure, using default configuration.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { bundleFromString, createConfig } from '@redocly/openapi-core';
|
||||||
|
|
||||||
|
const config = await createConfig({}); // create empty config
|
||||||
|
const source = stringifyYaml({ openapi: '3.0.1' /* ... */ }); // you can also use JSON.stringify
|
||||||
|
const bundleResults = await bundleFromString({
|
||||||
|
source,
|
||||||
|
// optionally pass path to the file for resolving $refs and proper error locations
|
||||||
|
absoluteRef: 'optional/path/to/openapi.yaml',
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `createConfig`
|
||||||
|
|
||||||
|
Creates a config object from a JSON or YAML string or JS object.
|
||||||
|
Resolves remote config from `extends` (if there are URLs or local fs paths).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function createConfig(
|
||||||
|
// JSON or YAML string or object with Redocly config
|
||||||
|
config: string | RawUniversalConfig,
|
||||||
|
options?: {
|
||||||
|
// optional path to the config file for resolving $refs and proper error locations
|
||||||
|
configPath?: string;
|
||||||
|
}
|
||||||
|
): Promise<Config>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `loadConfig`
|
||||||
|
|
||||||
|
Loads a config object from a file system. If `configPath` is not provided,
|
||||||
|
it tries to find `redocly.yaml` in the current working directory.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function loadConfig(options?: {
|
||||||
|
// optional path to the config file for resolving $refs and proper error locations
|
||||||
|
configPath?: string;
|
||||||
|
// allows to add custom `extends` additionally to one from the config file
|
||||||
|
customExtends?: string[];
|
||||||
|
}): Promise<Config>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `lint`
|
||||||
|
|
||||||
|
Lint an OpenAPI document from the file system.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function lint(options: {
|
||||||
|
// path to the OpenAPI document root
|
||||||
|
ref: string;
|
||||||
|
// config object
|
||||||
|
config: Config;
|
||||||
|
}): Promise<NormalizedProblem[]>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `lintFromString`
|
||||||
|
|
||||||
|
Lint an OpenAPI document from a string.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function lintFromString(options: {
|
||||||
|
// OpenAPI document string
|
||||||
|
source: string;
|
||||||
|
// optional path to the OpenAPI document for resolving $refs and proper error locations
|
||||||
|
absoluteRef?: string;
|
||||||
|
// config object
|
||||||
|
config: Config;
|
||||||
|
}): Promise<NormalizedProblem[]>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `bundle`
|
||||||
|
|
||||||
|
Bundle an OpenAPI document from the file system.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function bundle(options: {
|
||||||
|
// path to the OpenAPI document root
|
||||||
|
ref: string;
|
||||||
|
// config object
|
||||||
|
config: Config;
|
||||||
|
// whether to fully dereference $refs, resulting document won't have any $ref
|
||||||
|
// warning: this can produce circular objects
|
||||||
|
dereference?: boolean;
|
||||||
|
// whether to remove unused components (schemas, parameters, responses, etc)
|
||||||
|
removeUnusedComponents?: boolean;
|
||||||
|
// whether to keep $ref pointers to the http URLs and resolve only local fs $refs
|
||||||
|
keepUrlRefs?: boolean;
|
||||||
|
}): Promise<{
|
||||||
|
bundle: {
|
||||||
|
parsed: object; // OpenAPI document object as js object
|
||||||
|
};
|
||||||
|
problems: NormalizedProblem[]
|
||||||
|
fileDependencies
|
||||||
|
rootType
|
||||||
|
refTypes
|
||||||
|
visitorsData
|
||||||
|
|
||||||
|
}>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `bundleFromString`
|
||||||
|
|
||||||
|
Bundle an OpenAPI document from a string.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
async function bundleFromString(options: {
|
||||||
|
// OpenAPI document string
|
||||||
|
source: string;
|
||||||
|
// optional path to the OpenAPI document for resolving $refs and proper error locations
|
||||||
|
absoluteRef?: string;
|
||||||
|
// config object
|
||||||
|
config: Config;
|
||||||
|
// whether to fully dereference $refs, resulting document won't have any $ref
|
||||||
|
// warning: this can produce circular objects
|
||||||
|
dereference?: boolean;
|
||||||
|
// whether to remove unused components (schemas, parameters, responses, etc)
|
||||||
|
removeUnusedComponents?: boolean;
|
||||||
|
// whether to keep $ref pointers to the http URLs and resolve only local fs $refs
|
||||||
|
keepUrlRefs?: boolean;
|
||||||
|
}): Promise<{
|
||||||
|
bundle: {
|
||||||
|
parsed: object; // OpenAPI document object as js object
|
||||||
|
};
|
||||||
|
problems: NormalizedProblem[]
|
||||||
|
fileDependencies
|
||||||
|
rootType
|
||||||
|
refTypes
|
||||||
|
visitorsData
|
||||||
|
|
||||||
|
}>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### `stringifyYaml`
|
||||||
|
|
||||||
|
Helper function to stringify a javascript object to YAML.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function stringifyYaml(obj: object): string;
|
||||||
|
```
|
||||||
@@ -308,7 +308,7 @@ components:
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`bundle should not place referened schema inline when component in question is not of type "schemas" 1`] = `
|
exports[`bundle should not place referenced schema inline when component in question is not of type "schemas" 1`] = `
|
||||||
openapi: 3.0.0
|
openapi: 3.0.0
|
||||||
paths:
|
paths:
|
||||||
/pet:
|
/pet:
|
||||||
|
|||||||
@@ -1,11 +1,32 @@
|
|||||||
import outdent from 'outdent';
|
import outdent from 'outdent';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { bundleDocument, bundle } from '../bundle';
|
import { bundleDocument, bundle, bundleFromString } from '../bundle';
|
||||||
import { parseYamlToDocument, yamlSerializer, makeConfig } from '../../__tests__/utils';
|
import { parseYamlToDocument, yamlSerializer, makeConfig } from '../../__tests__/utils';
|
||||||
import { StyleguideConfig, Config, ResolvedConfig } from '../config';
|
import { StyleguideConfig, Config, ResolvedConfig, createConfig, loadConfig } from '../config';
|
||||||
import { BaseResolver } from '../resolve';
|
import { BaseResolver } from '../resolve';
|
||||||
|
|
||||||
|
const stringDocument = outdent`
|
||||||
|
openapi: 3.0.0
|
||||||
|
paths:
|
||||||
|
/pet:
|
||||||
|
get:
|
||||||
|
operationId: get
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/shared_a'
|
||||||
|
- name: get_b
|
||||||
|
post:
|
||||||
|
operationId: post
|
||||||
|
parameters:
|
||||||
|
- $ref: '#/components/parameters/shared_a'
|
||||||
|
components:
|
||||||
|
parameters:
|
||||||
|
shared_a:
|
||||||
|
name: shared-a
|
||||||
|
`;
|
||||||
|
|
||||||
|
const testDocument = parseYamlToDocument(stringDocument, '');
|
||||||
|
|
||||||
describe('bundle', () => {
|
describe('bundle', () => {
|
||||||
const fetchMock = jest.fn(() =>
|
const fetchMock = jest.fn(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
@@ -19,28 +40,6 @@ describe('bundle', () => {
|
|||||||
|
|
||||||
expect.addSnapshotSerializer(yamlSerializer);
|
expect.addSnapshotSerializer(yamlSerializer);
|
||||||
|
|
||||||
const testDocument = parseYamlToDocument(
|
|
||||||
outdent`
|
|
||||||
openapi: 3.0.0
|
|
||||||
paths:
|
|
||||||
/pet:
|
|
||||||
get:
|
|
||||||
operationId: get
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/shared_a'
|
|
||||||
- name: get_b
|
|
||||||
post:
|
|
||||||
operationId: post
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/shared_a'
|
|
||||||
components:
|
|
||||||
parameters:
|
|
||||||
shared_a:
|
|
||||||
name: shared-a
|
|
||||||
`,
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
it('change nothing with only internal refs', async () => {
|
it('change nothing with only internal refs', async () => {
|
||||||
const { bundle, problems } = await bundleDocument({
|
const { bundle, problems } = await bundleDocument({
|
||||||
document: testDocument,
|
document: testDocument,
|
||||||
@@ -97,7 +96,7 @@ describe('bundle', () => {
|
|||||||
expect(res.parsed).toMatchSnapshot();
|
expect(res.parsed).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not place referened schema inline when component in question is not of type "schemas"', async () => {
|
it('should not place referenced schema inline when component in question is not of type "schemas"', async () => {
|
||||||
const { bundle: res, problems } = await bundle({
|
const { bundle: res, problems } = await bundle({
|
||||||
config: new Config({} as ResolvedConfig),
|
config: new Config({} as ResolvedConfig),
|
||||||
ref: path.join(__dirname, 'fixtures/refs/external-request-body.yaml'),
|
ref: path.join(__dirname, 'fixtures/refs/external-request-body.yaml'),
|
||||||
@@ -233,4 +232,45 @@ describe('bundle', () => {
|
|||||||
|
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw an error when there is no document to bundle', () => {
|
||||||
|
const wrapper = () =>
|
||||||
|
bundle({
|
||||||
|
config: new Config({} as ResolvedConfig),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper()).rejects.toThrowError('Document or reference is required.\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should bundle with a doc provided', async () => {
|
||||||
|
const {
|
||||||
|
bundle: { parsed },
|
||||||
|
problems,
|
||||||
|
} = await bundle({
|
||||||
|
config: await loadConfig({ configPath: path.join(__dirname, 'fixtures/redocly.yaml') }),
|
||||||
|
doc: testDocument,
|
||||||
|
});
|
||||||
|
|
||||||
|
const origCopy = JSON.parse(JSON.stringify(testDocument.parsed));
|
||||||
|
|
||||||
|
expect(problems).toHaveLength(0);
|
||||||
|
expect(parsed).toEqual(origCopy);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bundleFromString', () => {
|
||||||
|
it('should bundle from string using bundleFromString', async () => {
|
||||||
|
const {
|
||||||
|
bundle: { parsed, ...rest },
|
||||||
|
problems,
|
||||||
|
} = await bundleFromString({
|
||||||
|
config: await createConfig(`
|
||||||
|
extends:
|
||||||
|
- recommended
|
||||||
|
`),
|
||||||
|
source: testDocument.source.body,
|
||||||
|
});
|
||||||
|
expect(problems).toHaveLength(0);
|
||||||
|
expect(rest.source.body).toEqual(stringDocument);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
10
packages/core/src/__tests__/fixtures/lint/openapi.yaml
Normal file
10
packages/core/src/__tests__/fixtures/lint/openapi.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: Test API
|
||||||
|
version: '1.0'
|
||||||
|
description: Test
|
||||||
|
license: Fail
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: http://redocly-example.com
|
||||||
|
paths: {}
|
||||||
2
packages/core/src/__tests__/fixtures/redocly.yaml
Normal file
2
packages/core/src/__tests__/fixtures/redocly.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
extends:
|
||||||
|
- recommended
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { outdent } from 'outdent';
|
import { outdent } from 'outdent';
|
||||||
|
|
||||||
import { lintFromString, lintConfig, lintDocument } from '../lint';
|
import { lintFromString, lintConfig, lintDocument, lint } from '../lint';
|
||||||
import { BaseResolver } from '../resolve';
|
import { BaseResolver } from '../resolve';
|
||||||
import { loadConfig } from '../config/load';
|
import { loadConfig } from '../config/load';
|
||||||
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
|
import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
|
||||||
@@ -46,6 +46,35 @@ describe('lint', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('lint should work', async () => {
|
||||||
|
const results = await lint({
|
||||||
|
ref: path.join(__dirname, 'fixtures/lint/openapi.yaml'),
|
||||||
|
config: await loadConfig({
|
||||||
|
configPath: path.join(__dirname, 'fixtures/redocly.yaml'),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(replaceSourceWithRef(results, path.join(__dirname, 'fixtures/lint/')))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"from": undefined,
|
||||||
|
"location": Array [
|
||||||
|
Object {
|
||||||
|
"pointer": "#/info/license",
|
||||||
|
"reportOnKey": false,
|
||||||
|
"source": "openapi.yaml",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"message": "Expected type \`License\` (object) but got \`string\`",
|
||||||
|
"ruleId": "spec",
|
||||||
|
"severity": "error",
|
||||||
|
"suggest": Array [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
it('lintConfig should work', async () => {
|
it('lintConfig should work', async () => {
|
||||||
const document = parseYamlToDocument(
|
const document = parseYamlToDocument(
|
||||||
outdent`
|
outdent`
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import isEqual = require('lodash.isequal');
|
import isEqual = require('lodash.isequal');
|
||||||
import { BaseResolver, resolveDocument, Document, ResolvedRefMap, makeRefId } from './resolve';
|
import {
|
||||||
|
BaseResolver,
|
||||||
|
resolveDocument,
|
||||||
|
Document,
|
||||||
|
ResolvedRefMap,
|
||||||
|
makeRefId,
|
||||||
|
makeDocumentFromString,
|
||||||
|
} from './resolve';
|
||||||
import { Oas3Rule, normalizeVisitors, Oas3Visitor, Oas2Visitor } from './visitors';
|
import { Oas3Rule, normalizeVisitors, Oas3Visitor, Oas2Visitor } from './visitors';
|
||||||
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
||||||
import { WalkContext, walkDocument, UserContext, ResolveResult, NormalizedProblem } from './walk';
|
import { WalkContext, walkDocument, UserContext, ResolveResult, NormalizedProblem } from './walk';
|
||||||
@@ -22,10 +29,7 @@ export enum OasVersion {
|
|||||||
Version3_0 = 'oas3_0',
|
Version3_0 = 'oas3_0',
|
||||||
Version3_1 = 'oas3_1',
|
Version3_1 = 'oas3_1',
|
||||||
}
|
}
|
||||||
|
export type BundleOptions = {
|
||||||
export async function bundle(opts: {
|
|
||||||
ref?: string;
|
|
||||||
doc?: Document;
|
|
||||||
externalRefResolver?: BaseResolver;
|
externalRefResolver?: BaseResolver;
|
||||||
config: Config;
|
config: Config;
|
||||||
dereference?: boolean;
|
dereference?: boolean;
|
||||||
@@ -33,7 +37,14 @@ export async function bundle(opts: {
|
|||||||
skipRedoclyRegistryRefs?: boolean;
|
skipRedoclyRegistryRefs?: boolean;
|
||||||
removeUnusedComponents?: boolean;
|
removeUnusedComponents?: boolean;
|
||||||
keepUrlRefs?: boolean;
|
keepUrlRefs?: boolean;
|
||||||
}) {
|
};
|
||||||
|
|
||||||
|
export async function bundle(
|
||||||
|
opts: {
|
||||||
|
ref?: string;
|
||||||
|
doc?: Document;
|
||||||
|
} & BundleOptions
|
||||||
|
) {
|
||||||
const {
|
const {
|
||||||
ref,
|
ref,
|
||||||
doc,
|
doc,
|
||||||
@@ -45,7 +56,7 @@ export async function bundle(opts: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const document =
|
const document =
|
||||||
doc !== undefined ? doc : await externalRefResolver.resolveDocument(base, ref!, true);
|
doc === undefined ? await externalRefResolver.resolveDocument(base, ref!, true) : doc;
|
||||||
|
|
||||||
if (document instanceof Error) {
|
if (document instanceof Error) {
|
||||||
throw document;
|
throw document;
|
||||||
@@ -59,6 +70,23 @@ export async function bundle(opts: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function bundleFromString(
|
||||||
|
opts: {
|
||||||
|
source: string;
|
||||||
|
absoluteRef?: string;
|
||||||
|
} & BundleOptions
|
||||||
|
) {
|
||||||
|
const { source, absoluteRef, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts;
|
||||||
|
const document = makeDocumentFromString(source, absoluteRef || '/');
|
||||||
|
|
||||||
|
return bundleDocument({
|
||||||
|
document,
|
||||||
|
...opts,
|
||||||
|
externalRefResolver,
|
||||||
|
config: opts.config.styleguide,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
type BundleContext = WalkContext;
|
type BundleContext = WalkContext;
|
||||||
|
|
||||||
export type BundleResult = {
|
export type BundleResult = {
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ describe('findConfig', () => {
|
|||||||
.spyOn(fs, 'existsSync')
|
.spyOn(fs, 'existsSync')
|
||||||
.mockImplementation((name) => name === 'redocly.yaml' || name === '.redocly.yaml');
|
.mockImplementation((name) => name === 'redocly.yaml' || name === '.redocly.yaml');
|
||||||
expect(findConfig).toThrow(`
|
expect(findConfig).toThrow(`
|
||||||
Multiple configuration files are not allowed.
|
Multiple configuration files are not allowed.
|
||||||
Found the following files: redocly.yaml, .redocly.yaml.
|
Found the following files: redocly.yaml, .redocly.yaml.
|
||||||
Please use 'redocly.yaml' instead.
|
Please use 'redocly.yaml' instead.
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
@@ -124,6 +124,39 @@ describe('createConfig', () => {
|
|||||||
overridesRules: rawConfig.rules as Record<string, RuleConfig>,
|
overridesRules: rawConfig.rules as Record<string, RuleConfig>,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create config from object with a custom plugin', async () => {
|
||||||
|
const testCustomRule = jest.fn();
|
||||||
|
const rawConfig: FlatRawConfig = {
|
||||||
|
extends: [],
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
id: 'my-plugin',
|
||||||
|
rules: {
|
||||||
|
oas3: {
|
||||||
|
'test-rule': testCustomRule,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'my-plugin/test-rule': 'error',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const config = await createConfig(rawConfig);
|
||||||
|
|
||||||
|
expect(config.styleguide.plugins[0]).toEqual({
|
||||||
|
id: 'my-plugin',
|
||||||
|
rules: {
|
||||||
|
oas3: {
|
||||||
|
'my-plugin/test-rule': testCustomRule,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(config.styleguide.rules.oas3_0).toEqual({
|
||||||
|
'my-plugin/test-rule': 'error',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function verifyExtendedConfig(
|
function verifyExtendedConfig(
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ import { Config, DOMAINS } from './config';
|
|||||||
import { transformConfig } from './utils';
|
import { transformConfig } from './utils';
|
||||||
import { resolveConfig } from './config-resolvers';
|
import { resolveConfig } from './config-resolvers';
|
||||||
|
|
||||||
import type { DeprecatedInRawConfig, FlatRawConfig, RawConfig, Region } from './types';
|
import type {
|
||||||
|
DeprecatedInRawConfig,
|
||||||
|
FlatRawConfig,
|
||||||
|
RawConfig,
|
||||||
|
RawUniversalConfig,
|
||||||
|
Region,
|
||||||
|
} from './types';
|
||||||
import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
||||||
|
|
||||||
async function addConfigMetadata({
|
async function addConfigMetadata({
|
||||||
@@ -101,8 +107,8 @@ export function findConfig(dir?: string): string | undefined {
|
|||||||
).filter(fs.existsSync);
|
).filter(fs.existsSync);
|
||||||
if (existingConfigFiles.length > 1) {
|
if (existingConfigFiles.length > 1) {
|
||||||
throw new Error(`
|
throw new Error(`
|
||||||
Multiple configuration files are not allowed.
|
Multiple configuration files are not allowed.
|
||||||
Found the following files: ${existingConfigFiles.join(', ')}.
|
Found the following files: ${existingConfigFiles.join(', ')}.
|
||||||
Please use 'redocly.yaml' instead.
|
Please use 'redocly.yaml' instead.
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
@@ -129,10 +135,11 @@ export async function getConfig(
|
|||||||
type CreateConfigOptions = {
|
type CreateConfigOptions = {
|
||||||
extends?: string[];
|
extends?: string[];
|
||||||
tokens?: RegionalTokenWithValidity[];
|
tokens?: RegionalTokenWithValidity[];
|
||||||
|
configPath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function createConfig(
|
export async function createConfig(
|
||||||
config: string | RawConfig,
|
config: string | RawUniversalConfig,
|
||||||
options?: CreateConfigOptions
|
options?: CreateConfigOptions
|
||||||
): Promise<Config> {
|
): Promise<Config> {
|
||||||
return addConfigMetadata({
|
return addConfigMetadata({
|
||||||
|
|||||||
@@ -185,6 +185,9 @@ export type RawConfig = {
|
|||||||
telemetry?: Telemetry;
|
telemetry?: Telemetry;
|
||||||
} & ThemeConfig;
|
} & ThemeConfig;
|
||||||
|
|
||||||
|
// RawConfig is legacy, use RawUniversalConfig in public APIs
|
||||||
|
export type RawUniversalConfig = Omit<RawConfig, 'styleguide'> & StyleguideRawConfig;
|
||||||
|
|
||||||
export type FlatApi = Omit<Api, 'styleguide'> &
|
export type FlatApi = Omit<Api, 'styleguide'> &
|
||||||
Omit<ApiStyleguideRawConfig, 'doNotResolveExamples'>;
|
Omit<ApiStyleguideRawConfig, 'doNotResolveExamples'>;
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export {
|
|||||||
Config,
|
Config,
|
||||||
StyleguideConfig,
|
StyleguideConfig,
|
||||||
RawConfig,
|
RawConfig,
|
||||||
|
RawUniversalConfig,
|
||||||
IGNORE_FILE,
|
IGNORE_FILE,
|
||||||
Region,
|
Region,
|
||||||
getMergedConfig,
|
getMergedConfig,
|
||||||
@@ -75,4 +76,4 @@ export {
|
|||||||
export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
|
export { getAstNodeByPointer, getLineColLocation } from './format/codeframes';
|
||||||
export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
|
export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
|
||||||
export { lint, lint as validate, lintDocument, lintFromString, lintConfig } from './lint';
|
export { lint, lint as validate, lintDocument, lintFromString, lintConfig } from './lint';
|
||||||
export { bundle, bundleDocument, mapTypeToComponent } from './bundle';
|
export { bundle, bundleDocument, mapTypeToComponent, bundleFromString } from './bundle';
|
||||||
|
|||||||
Reference in New Issue
Block a user