@redocly/openapi-core 1.0.0-beta.49 → 1.0.0-beta.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/__tests__/normalizeVisitors.test.ts +1 -1
  2. package/__tests__/utils.ts +1 -1
  3. package/__tests__/walk.test.ts +77 -4
  4. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -1
  5. package/lib/benchmark/utils.d.ts +1 -1
  6. package/lib/bundle.d.ts +1 -1
  7. package/lib/bundle.js +10 -10
  8. package/lib/config/builtIn.d.ts +2 -1
  9. package/lib/config/builtIn.js +9 -1
  10. package/lib/config/config.d.ts +5 -7
  11. package/lib/config/config.js +40 -100
  12. package/lib/config/load.d.ts +2 -0
  13. package/lib/config/load.js +65 -0
  14. package/lib/config/rules.d.ts +1 -1
  15. package/lib/format/codeframes.js +1 -1
  16. package/lib/index.d.ts +4 -3
  17. package/lib/index.js +13 -11
  18. package/lib/lint.d.ts +6 -19
  19. package/lib/lint.js +13 -43
  20. package/lib/oas-types.d.ts +19 -0
  21. package/lib/oas-types.js +42 -0
  22. package/lib/resolve.d.ts +6 -2
  23. package/lib/resolve.js +18 -5
  24. package/lib/rules/ajv.d.ts +3 -3
  25. package/lib/rules/ajv.js +21 -18
  26. package/lib/rules/common/info-contact.js +2 -1
  27. package/lib/rules/common/info-description.js +2 -1
  28. package/lib/rules/common/info-license-url.js +2 -1
  29. package/lib/rules/common/license-url.js +2 -1
  30. package/lib/rules/common/no-ambiguous-paths.js +2 -1
  31. package/lib/rules/common/no-enum-type-mismatch.js +2 -1
  32. package/lib/rules/common/no-identical-paths.js +2 -1
  33. package/lib/rules/common/no-path-trailing-slash.js +2 -1
  34. package/lib/rules/common/operation-2xx-response.js +2 -1
  35. package/lib/rules/common/operation-description.js +2 -1
  36. package/lib/rules/common/operation-operationId-unique.js +2 -1
  37. package/lib/rules/common/operation-operationId-url-safe.js +2 -1
  38. package/lib/rules/common/operation-operationId.js +9 -4
  39. package/lib/rules/common/operation-parameters-unique.js +2 -1
  40. package/lib/rules/common/operation-security-defined.js +2 -1
  41. package/lib/rules/common/operation-singular-tag.js +2 -1
  42. package/lib/rules/common/operation-summary.js +2 -1
  43. package/lib/rules/common/operation-tag-defined.js +2 -1
  44. package/lib/rules/common/parameter-description.js +2 -1
  45. package/lib/rules/common/path-declaration-must-exist.js +2 -1
  46. package/lib/rules/common/path-http-verbs-order.js +2 -1
  47. package/lib/rules/common/path-not-include-query.js +2 -1
  48. package/lib/rules/common/path-params-defined.js +3 -2
  49. package/lib/rules/common/paths-kebab-case.js +2 -1
  50. package/lib/rules/common/registry-dependencies.js +2 -1
  51. package/lib/rules/common/spec.js +5 -2
  52. package/lib/rules/common/tag-description.js +2 -1
  53. package/lib/rules/common/tags-alphabetical.js +2 -1
  54. package/lib/rules/no-unresolved-refs.js +2 -1
  55. package/lib/rules/oas2/boolean-parameter-prefixes.js +2 -1
  56. package/lib/rules/oas2/index.d.ts +1 -1
  57. package/lib/rules/oas2/index.js +1 -1
  58. package/lib/rules/oas3/boolean-parameter-prefixes.js +2 -1
  59. package/lib/rules/oas3/index.d.ts +2 -1
  60. package/lib/rules/oas3/index.js +2 -2
  61. package/lib/rules/oas3/no-empty-servers.js +2 -1
  62. package/lib/rules/oas3/no-example-value-and-externalValue.js +2 -1
  63. package/lib/rules/oas3/no-invalid-media-type-examples.js +20 -10
  64. package/lib/rules/oas3/no-server-example.com.js +2 -1
  65. package/lib/rules/oas3/no-server-trailing-slash.js +2 -1
  66. package/lib/rules/oas3/no-servers-empty-enum.js +2 -1
  67. package/lib/rules/oas3/no-undefined-server-variable.js +2 -1
  68. package/lib/rules/oas3/no-unused-components.js +2 -1
  69. package/lib/rules/other/stats.js +2 -1
  70. package/lib/types/oas2.js +1 -1
  71. package/lib/types/oas3.js +1 -1
  72. package/lib/types/oas3_1.js +1 -1
  73. package/lib/utils.d.ts +1 -1
  74. package/lib/visitors.d.ts +3 -1
  75. package/lib/visitors.js +4 -4
  76. package/lib/walk.d.ts +1 -1
  77. package/lib/walk.js +11 -2
  78. package/package.json +7 -7
  79. package/src/__tests__/lint.test.ts +44 -0
  80. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -1
  81. package/src/benchmark/benchmark.js +3 -1
  82. package/src/benchmark/utils.ts +1 -1
  83. package/src/bundle.ts +2 -2
  84. package/src/config/__tests__/resolve-plugins.test.ts +1 -1
  85. package/src/config/builtIn.ts +11 -1
  86. package/src/config/config.ts +30 -78
  87. package/src/config/load.ts +60 -0
  88. package/src/config/rules.ts +1 -1
  89. package/src/format/codeframes.ts +1 -1
  90. package/src/index.ts +4 -3
  91. package/src/lint.ts +19 -56
  92. package/src/oas-types.ts +58 -0
  93. package/src/resolve.ts +17 -4
  94. package/src/rules/__tests__/config.ts +10 -0
  95. package/src/rules/__tests__/no-unresolved-refs.test.ts +9 -21
  96. package/src/rules/ajv.ts +27 -23
  97. package/src/rules/common/__tests__/info-description.test.ts +8 -16
  98. package/src/rules/common/__tests__/info-license.test.ts +3 -3
  99. package/src/rules/common/__tests__/license-url.test.ts +3 -3
  100. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +2 -2
  101. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +50 -5
  102. package/src/rules/common/__tests__/no-identical-paths.test.ts +2 -2
  103. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +4 -4
  104. package/src/rules/common/__tests__/operation-2xx-response.test.ts +4 -4
  105. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +3 -3
  106. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +2 -5
  107. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +5 -17
  108. package/src/rules/common/__tests__/operation-security-defined.test.ts +3 -3
  109. package/src/rules/common/__tests__/operation-singular-tag.test.ts +3 -3
  110. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +3 -3
  111. package/src/rules/common/__tests__/path-not-include-query.test.ts +3 -3
  112. package/src/rules/common/__tests__/path-params-defined.test.ts +4 -4
  113. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  114. package/src/rules/common/__tests__/tag-description.test.ts +3 -3
  115. package/src/rules/common/__tests__/tags-alphabetical.test.ts +3 -3
  116. package/src/rules/common/operation-operationId.ts +7 -3
  117. package/src/rules/common/spec.ts +4 -1
  118. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +7 -10
  119. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +3 -6
  120. package/src/rules/oas2/__tests__/spec/utils.ts +2 -0
  121. package/src/rules/oas2/index.ts +1 -10
  122. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +7 -10
  123. package/src/rules/oas3/__tests__/fixtures/common.yaml +11 -0
  124. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +7 -7
  125. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +3 -9
  126. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +103 -29
  127. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +3 -3
  128. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  129. package/src/rules/oas3/__tests__/no-unused-components.test.ts +2 -2
  130. package/src/rules/oas3/__tests__/spec/spec.test.ts +3 -1
  131. package/src/rules/oas3/__tests__/spec/utils.ts +2 -0
  132. package/src/rules/oas3/index.ts +3 -4
  133. package/src/rules/oas3/no-invalid-media-type-examples.ts +27 -19
  134. package/src/visitors.ts +12 -8
  135. package/src/walk.ts +24 -8
  136. package/tsconfig.tsbuildinfo +1 -3084
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.49",
3
+ "version": "1.0.0-beta.53",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -30,22 +30,22 @@
30
30
  "Andriy Leliv <andriy@redoc.ly> (https://redoc.ly/)"
31
31
  ],
32
32
  "dependencies": {
33
+ "@redocly/ajv": "^8.6.2",
33
34
  "@types/node": "^14.11.8",
34
35
  "colorette": "^1.2.0",
36
+ "js-levenshtein": "^1.1.6",
35
37
  "js-yaml": "^3.14.1",
36
- "yaml-ast-parser": "0.0.43",
38
+ "lodash.isequal": "^4.5.0",
37
39
  "minimatch": "^3.0.4",
38
40
  "node-fetch": "^2.6.1",
39
- "js-levenshtein": "^1.1.6",
40
- "@redocly/ajv": "^6.12.3",
41
- "lodash.isequal": "^4.5.0"
41
+ "yaml-ast-parser": "0.0.43"
42
42
  },
43
43
  "devDependencies": {
44
+ "@types/js-levenshtein": "^1.1.0",
44
45
  "@types/js-yaml": "^3.12.4",
46
+ "@types/lodash.isequal": "^4.5.5",
45
47
  "@types/minimatch": "^3.0.3",
46
48
  "@types/node-fetch": "^2.5.7",
47
- "@types/js-levenshtein": "^1.1.0",
48
- "@types/lodash.isequal": "^4.5.5",
49
49
  "typescript": "^4.0.5"
50
50
  }
51
51
  }
@@ -0,0 +1,44 @@
1
+ import { outdent } from 'outdent';
2
+
3
+ import { lintFromString } from '../lint';
4
+ import { loadConfig } from '../config/load';
5
+ import { replaceSourceWithRef } from '../../__tests__/utils';
6
+
7
+ describe('lint', () => {
8
+ it('lintFromString should work', async () => {
9
+ const results = await lintFromString({
10
+ absoluteRef: '/test/spec.yaml',
11
+ source: outdent`
12
+ openapi: 3.0.0
13
+ info:
14
+ title: Test API
15
+ version: "1.0"
16
+ description: Test
17
+ license: Fail
18
+
19
+ servers:
20
+ - url: http://example.com
21
+ paths: {}
22
+ `,
23
+ config: await loadConfig(),
24
+ });
25
+
26
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
27
+ Array [
28
+ Object {
29
+ "location": Array [
30
+ Object {
31
+ "pointer": "#/info/license",
32
+ "reportOnKey": false,
33
+ "source": "/test/spec.yaml",
34
+ },
35
+ ],
36
+ "message": "Expected type \`License\` (object) but got \`string\`",
37
+ "ruleId": "spec",
38
+ "severity": "error",
39
+ "suggest": Array [],
40
+ },
41
+ ]
42
+ `);
43
+ });
44
+ });
@@ -4,6 +4,7 @@ import { lintDocument } from '../../lint';
4
4
  import { LintConfig } from '../../config/config';
5
5
  import { BaseResolver } from '../../resolve';
6
6
  import { parseYamlToDocument } from '../utils';
7
+ import { defaultPlugin } from '../../config/builtIn';
7
8
 
8
9
  export const name = 'Validate with recommended rules';
9
10
  export const count = 10;
@@ -17,6 +18,6 @@ export function measureAsync() {
17
18
  return lintDocument({
18
19
  externalRefResolver: new BaseResolver(),
19
20
  document: rebillyDocument,
20
- config: new LintConfig({}),
21
+ config: new LintConfig({ plugins: [defaultPlugin] }),
21
22
  });
22
23
  }
@@ -30,7 +30,9 @@ function prepareRevision(revision) {
30
30
  // Returns the complete git hash for a given git revision reference.
31
31
  const hash = exec(`git rev-parse "${revision}"`);
32
32
  const dir = path.join(os.tmpdir(), 'openapi-cli-benchmark', hash);
33
- fs.rmdirSync(dir, { recursive: true });
33
+ if (fs.existsSync(dir)) {
34
+ fs.rmdirSync(dir, { recursive: true});
35
+ }
34
36
  fs.mkdirSync(dir, { recursive: true });
35
37
 
36
38
  exec(`git archive "${hash}" | tar -xC "${dir}"`);
@@ -1,6 +1,6 @@
1
1
  import * as yaml from 'js-yaml';
2
2
  import { Document, Source } from '../resolve';
3
- import { Oas3RuleSet } from '../lint';
3
+ import { Oas3RuleSet } from '../oas-types';
4
4
  import { RuleConfig, LintConfig, Plugin } from '../config/config';
5
5
 
6
6
  export function parseYamlToDocument(body: string, absoluteRef: string = ''): Document {
package/src/bundle.ts CHANGED
@@ -6,9 +6,9 @@ import { Oas2Types } from './types/oas2';
6
6
  import { Oas3_1Types } from './types/oas3_1';
7
7
  import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
8
8
  import { WalkContext, walkDocument, UserContext, ResolveResult } from './walk';
9
- import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './lint';
9
+ import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
10
10
  import { Location, refBaseName } from './ref-utils';
11
- import { Config, LintConfig } from './config/config';
11
+ import type { Config, LintConfig } from './config/config';
12
12
  import { initRules } from './config/rules';
13
13
  import { reportUnresolvedRef } from './rules/no-unresolved-refs';
14
14
  import { isPlainObject } from './utils';
@@ -1,5 +1,5 @@
1
1
  import * as path from 'path';
2
- import { loadConfig } from '../config';
2
+ import { loadConfig } from '../load';
3
3
 
4
4
  describe('resolving a plugin', () => {
5
5
  const configPath = path.join(__dirname, 'fixtures/plugin-config.yaml');
@@ -1,7 +1,9 @@
1
1
  import recommended from './recommended';
2
2
  import all from './all';
3
3
  import minimal from './minimal';
4
- import { LintRawConfig } from './config';
4
+ import { LintRawConfig, Plugin } from './config';
5
+
6
+ import * as builtinRules from '../rules/builtin';
5
7
 
6
8
  export const builtInConfigs: Record<string, LintRawConfig> = {
7
9
  recommended,
@@ -11,3 +13,11 @@ export const builtInConfigs: Record<string, LintRawConfig> = {
11
13
  decorators: { 'registry-dependencies': 'on' }
12
14
  }
13
15
  };
16
+
17
+ export const defaultPlugin: Plugin = {
18
+ id: '', // default plugin doesn't have id
19
+ rules: builtinRules.rules,
20
+ preprocessors: builtinRules.preprocessors,
21
+ decorators: builtinRules.decorators,
22
+ configs: builtInConfigs,
23
+ }
@@ -3,9 +3,8 @@ import * as path from 'path';
3
3
  import * as yaml from 'js-yaml';
4
4
  import { dirname } from 'path';
5
5
  import { red, blue } from 'colorette';
6
- import { builtInConfigs } from './builtIn';
7
- import * as builtinRules from '../rules/builtin';
8
- import { loadYaml, notUndefined } from '../utils';
6
+
7
+ import { notUndefined } from '../utils';
9
8
 
10
9
  import {
11
10
  OasVersion,
@@ -15,14 +14,13 @@ import {
15
14
  Oas2RuleSet,
16
15
  Oas2PreprocessorsSet,
17
16
  Oas2DecoratorsSet,
18
- } from '../lint';
17
+ Oas3RuleSet
18
+ } from '../oas-types';
19
19
 
20
20
  import { ProblemSeverity, NormalizedProblem } from '../walk';
21
- import { Oas3RuleSet } from '../lint';
22
21
 
23
22
  import recommended from './recommended';
24
23
  import { NodeType } from '../types';
25
- import { RedoclyClient } from '../redocly';
26
24
 
27
25
  export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
28
26
  const IGNORE_BANNER =
@@ -149,13 +147,6 @@ export class LintConfig {
149
147
  this.plugins = rawConfig.plugins ? resolvePlugins(rawConfig.plugins, configFile) : [];
150
148
  this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
151
149
 
152
- this.plugins.push({
153
- id: '', // default plugin doesn't have id
154
- rules: builtinRules.rules,
155
- preprocessors: builtinRules.preprocessors,
156
- decorators: builtinRules.decorators,
157
- });
158
-
159
150
  if (!rawConfig.extends) {
160
151
  this.recommendedFallback = true;
161
152
  }
@@ -192,7 +183,7 @@ export class LintConfig {
192
183
  [OasVersion.Version3_1]: { ...merged.decorators, ...merged.oas3_1Decorators },
193
184
  };
194
185
 
195
- const dir = this.configFile ? path.dirname(this.configFile) : process.cwd();
186
+ const dir = this.configFile ? path.dirname(this.configFile) : (typeof process !== 'undefined' && process.cwd() || '');
196
187
  const ignoreFile = path.join(dir, IGNORE_FILE);
197
188
 
198
189
  /* no crash when using it on the client */
@@ -260,6 +251,7 @@ export class LintConfig {
260
251
  if (plugin.typeExtension !== undefined) {
261
252
  switch (version) {
262
253
  case OasVersion.Version3_0:
254
+ case OasVersion.Version3_1:
263
255
  if (!plugin.typeExtension.oas3) continue;
264
256
  extendedTypes = plugin.typeExtension.oas3(extendedTypes, version);
265
257
  case OasVersion.Version2:
@@ -405,80 +397,38 @@ export class Config {
405
397
  }
406
398
  }
407
399
 
408
- export async function loadConfig(configPath?: string, customExtends?: string[]): Promise<Config> {
409
- if (configPath === undefined) {
410
- configPath = findConfig();
411
- }
412
- let rawConfig: RawConfig = {};
413
- // let resolvedPlugins: Plugin[] = [];
414
-
415
- if (configPath !== undefined) {
416
- try {
417
- rawConfig = (await loadYaml(configPath)) as RawConfig;
418
- } catch (e) {
419
- throw new Error(`Error parsing config file at \`${configPath}\`: ${e.message}`);
420
- }
421
- }
422
- if (customExtends !== undefined) {
423
- rawConfig.lint = rawConfig.lint || {};
424
- rawConfig.lint.extends = customExtends;
425
- }
426
-
427
- const redoclyClient = new RedoclyClient();
428
- if (redoclyClient.hasToken()) {
429
- if (!rawConfig.resolve) rawConfig.resolve = {};
430
- if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
431
- rawConfig.resolve.http.headers = [
432
- {
433
- matches: `https://api.${process.env.REDOCLY_DOMAIN || 'redoc.ly'}/registry/**`,
434
- name: 'Authorization',
435
- envVariable: undefined,
436
- value: (redoclyClient && (await redoclyClient.getAuthorizationHeader())) || '',
437
- },
438
- ...(rawConfig.resolve.http.headers ?? []),
439
- ];
440
- }
441
- return new Config(rawConfig, configPath);
442
- }
443
-
444
- function findConfig() {
445
- if (fs.existsSync('.redocly.yaml')) {
446
- return '.redocly.yaml';
447
- } else if (fs.existsSync('.redocly.yml')) {
448
- return '.redocly.yml';
449
- }
450
- return undefined;
451
- }
452
-
453
-
454
400
  function resolvePresets(presets: string[], plugins: Plugin[]) {
455
401
  return presets.map((presetName) => {
456
- let preset = builtInConfigs[presetName];
457
- if (!preset && presetName.indexOf('/') > -1) {
458
- const [pluginName, configName] = presetName.split('/');
459
- const plugin = plugins.find((p) => p.id === pluginName);
460
- if (!plugin) {
461
- throw new Error(`Invalid config ${red(presetName)}: plugin ${pluginName} is not included.`);
462
- }
402
+ const { pluginId, configName } = parsePresetName(presetName);
463
403
 
464
- preset = plugin.configs?.[configName]!;
465
- if (!preset) {
466
- throw new Error(
467
- `Invalid config ${red(
468
- presetName,
469
- )}: plugin ${pluginName} doesn't export config with name ${configName}.`,
470
- );
471
- }
472
- return preset;
404
+ const plugin = plugins.find((p) => p.id === pluginId);
405
+ if (!plugin) {
406
+ throw new Error(`Invalid config ${red(presetName)}: plugin ${pluginId} is not included.`);
473
407
  }
474
408
 
409
+ const preset = plugin.configs?.[configName]!;
475
410
  if (!preset) {
476
- throw new Error(`Invalid config ${red(presetName)}: there is no such built-in config.`);
411
+ throw new Error(
412
+ pluginId
413
+ ? `Invalid config ${red(
414
+ presetName,
415
+ )}: plugin ${pluginId} doesn't export config with name ${configName}.`
416
+ : `Invalid config ${red(presetName)}: there is no such built-in config.`,
417
+ );
477
418
  }
478
419
  return preset;
479
420
  });
480
421
  }
481
422
 
423
+ function parsePresetName(presetName: string): { pluginId: string; configName: string } {
424
+ if (presetName.indexOf('/') > -1) {
425
+ const [pluginId, configName] = presetName.split('/');
426
+ return { pluginId, configName };
427
+ } else {
428
+ return { pluginId: '', configName: presetName };
429
+ }
430
+ }
431
+
482
432
  function resolvePlugins(plugins: (string | Plugin)[] | null, configPath: string = ''): Plugin[] {
483
433
  if (!plugins) return [];
484
434
 
@@ -496,7 +446,7 @@ function resolvePlugins(plugins: (string | Plugin)[] | null, configPath: string
496
446
  : p;
497
447
 
498
448
  const id = pluginModule.id;
499
- if (!id) {
449
+ if (typeof id !== 'string') {
500
450
  throw new Error(red(`Plugin must define \`id\` property in ${blue(p.toString())}.`));
501
451
  }
502
452
 
@@ -565,6 +515,8 @@ function resolvePlugins(plugins: (string | Plugin)[] | null, configPath: string
565
515
  }
566
516
 
567
517
  function prefixRules<T extends Record<string, any>>(rules: T, prefix: string) {
518
+ if (!prefix) return rules;
519
+
568
520
  const res: any = {};
569
521
  for (const name of Object.keys(rules)) {
570
522
  res[`${prefix}/${name}`] = rules[name];
@@ -0,0 +1,60 @@
1
+ import * as fs from 'fs';
2
+
3
+ import { RedoclyClient } from '../redocly';
4
+ import { loadYaml } from '../utils';
5
+ import { Config, RawConfig } from './config';
6
+
7
+ import { defaultPlugin } from './builtIn';
8
+
9
+ export async function loadConfig(configPath?: string, customExtends?: string[]): Promise<Config> {
10
+ if (configPath === undefined) {
11
+ configPath = findConfig();
12
+ }
13
+ let rawConfig: RawConfig = {};
14
+
15
+ if (configPath !== undefined) {
16
+ try {
17
+ rawConfig = (await loadYaml(configPath)) as RawConfig;
18
+ } catch (e) {
19
+ throw new Error(`Error parsing config file at \`${configPath}\`: ${e.message}`);
20
+ }
21
+ }
22
+ if (customExtends !== undefined) {
23
+ rawConfig.lint = rawConfig.lint || {};
24
+ rawConfig.lint.extends = customExtends;
25
+ }
26
+
27
+ const redoclyClient = new RedoclyClient();
28
+ if (redoclyClient.hasToken()) {
29
+ if (!rawConfig.resolve) rawConfig.resolve = {};
30
+ if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
31
+ rawConfig.resolve.http.headers = [
32
+ {
33
+ matches: `https://api.${process.env.REDOCLY_DOMAIN || 'redoc.ly'}/registry/**`,
34
+ name: 'Authorization',
35
+ envVariable: undefined,
36
+ value: (redoclyClient && (await redoclyClient.getAuthorizationHeader())) || '',
37
+ },
38
+ ...(rawConfig.resolve.http.headers ?? []),
39
+ ];
40
+ }
41
+ return new Config(
42
+ {
43
+ ...rawConfig,
44
+ lint: {
45
+ ...rawConfig?.lint,
46
+ plugins: [...(rawConfig?.lint?.plugins || []), defaultPlugin], // inject default plugin
47
+ },
48
+ },
49
+ configPath,
50
+ );
51
+ }
52
+
53
+ function findConfig() {
54
+ if (fs.existsSync('.redocly.yaml')) {
55
+ return '.redocly.yaml';
56
+ } else if (fs.existsSync('.redocly.yml')) {
57
+ return '.redocly.yml';
58
+ }
59
+ return undefined;
60
+ }
@@ -1,4 +1,4 @@
1
- import { RuleSet, OasVersion } from '../lint';
1
+ import { RuleSet, OasVersion } from '../oas-types';
2
2
  import { LintConfig } from './config';
3
3
  import { notUndefined } from '../utils';
4
4
 
@@ -134,7 +134,7 @@ export function getLineColLocation(location: LocationObject): LineColLocationObj
134
134
  if (location.pointer === undefined) return location;
135
135
 
136
136
  const { source, pointer, reportOnKey } = location;
137
- const ast = source.getAst() as YAMLNode;
137
+ const ast = source.getAst(yamlAst.safeLoad) as YAMLNode;
138
138
  const astNode = getAstNodeByPointer(ast, pointer, !!reportOnKey);
139
139
  return {
140
140
  ...location,
package/src/index.ts CHANGED
@@ -8,14 +8,15 @@ export { StatsAccumulator, StatsName } from './typings/common';
8
8
  export { normalizeTypes } from './types';
9
9
  export { Stats } from './rules/other/stats';
10
10
 
11
- export { loadConfig, Config, LintConfig, RawConfig, IGNORE_FILE } from './config/config';
11
+ export { Config, LintConfig, RawConfig, IGNORE_FILE } from './config/config';
12
+ export { loadConfig } from './config/load';
12
13
  export { RedoclyClient } from './redocly';
13
14
  export { Source, BaseResolver, Document, resolveDocument, ResolveError, YamlParseError } from './resolve';
14
15
  export { unescapePointer } from './ref-utils';
15
- export { detectOpenAPI, OasMajorVersion, openAPIMajor } from './lint';
16
+ export { detectOpenAPI, OasMajorVersion, openAPIMajor, OasVersion } from './oas-types';
16
17
  export { normalizeVisitors } from './visitors';
17
18
  export { WalkContext, walkDocument, NormalizedProblem, ProblemSeverity, LineColLocationObject, LocationObject, Loc } from './walk';
18
19
 
19
20
  export { formatProblems, OutputFormat, getTotals, Totals } from './format/format';
20
- export { OasVersion, lint, lint as validate, lintDocument } from './lint';
21
+ export { lint, lint as validate, lintDocument, lintFromString } from './lint';
21
22
  export { bundle } from './bundle';
package/src/lint.ts CHANGED
@@ -1,10 +1,6 @@
1
- import { BaseResolver, resolveDocument, Document } from './resolve';
1
+ import { BaseResolver, resolveDocument, Document, makeDocumentFromString } from './resolve';
2
2
  import {
3
- Oas3Rule,
4
3
  normalizeVisitors,
5
- Oas3Preprocessor,
6
- Oas2Rule,
7
- Oas2Preprocessor,
8
4
  } from './visitors';
9
5
  import { Oas3_1Types } from './types/oas3_1';
10
6
  import { Oas3Types } from './types/oas3';
@@ -15,25 +11,7 @@ import { LintConfig, Config } from './config/config';
15
11
  import { normalizeTypes } from './types';
16
12
  import { initRules } from './config/rules';
17
13
  import { releaseAjvInstance } from './rules/ajv';
18
-
19
- export enum OasVersion {
20
- Version2 = 'oas2',
21
- Version3_0 = 'oas3_0',
22
- Version3_1 = 'oas3_1',
23
- }
24
-
25
- export enum OasMajorVersion {
26
- Version2 = 'oas2',
27
- Version3 = 'oas3',
28
- }
29
-
30
- export type RuleSet<T> = Record<string, T>;
31
- export type Oas3RuleSet = Record<string, Oas3Rule>;
32
- export type Oas2RuleSet = Record<string, Oas2Rule>;
33
- export type Oas3PreprocessorsSet = Record<string, Oas3Preprocessor>;
34
- export type Oas2PreprocessorsSet = Record<string, Oas2Preprocessor>;
35
- export type Oas3DecoratorsSet = Record<string, Oas3Preprocessor>;
36
- export type Oas2DecoratorsSet = Record<string, Oas2Preprocessor>;
14
+ import { detectOpenAPI, OasMajorVersion, OasVersion, openAPIMajor } from './oas-types';
37
15
 
38
16
  export async function lint(opts: {
39
17
  ref: string;
@@ -51,6 +29,23 @@ export async function lint(opts: {
51
29
  });
52
30
  }
53
31
 
32
+ export async function lintFromString(opts: {
33
+ source: string;
34
+ absoluteRef?: string;
35
+ config: Config;
36
+ externalRefResolver?: BaseResolver;
37
+ }) {
38
+ const { source, absoluteRef, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts;
39
+ const document = makeDocumentFromString(source, absoluteRef || '/');
40
+
41
+ return lintDocument({
42
+ document,
43
+ ...opts,
44
+ externalRefResolver,
45
+ config: opts.config.lint,
46
+ });
47
+ }
48
+
54
49
  export async function lintDocument(opts: {
55
50
  document: Document;
56
51
  config: LintConfig;
@@ -94,35 +89,3 @@ export async function lintDocument(opts: {
94
89
  });
95
90
  return ctx.problems.map((problem) => config.addProblemToIgnore(problem));
96
91
  }
97
-
98
- export function detectOpenAPI(root: any): OasVersion {
99
- if (typeof root !== 'object') {
100
- throw new Error(`Document must be JSON object, got ${typeof root}`);
101
- }
102
-
103
- if (!(root.openapi || root.swagger)) {
104
- throw new Error('This doesn’t look like an OpenAPI document.\n');
105
- }
106
-
107
- if (root.openapi && root.openapi.startsWith('3.0')) {
108
- return OasVersion.Version3_0;
109
- }
110
-
111
- if (root.openapi && root.openapi.startsWith('3.1')) {
112
- return OasVersion.Version3_1;
113
- }
114
-
115
- if (root.swagger && root.swagger === '2.0') {
116
- return OasVersion.Version2;
117
- }
118
-
119
- throw new Error(`Unsupported OpenAPI Version: ${root.openapi || root.swagger}`);
120
- }
121
-
122
- export function openAPIMajor(version: OasVersion): OasMajorVersion {
123
- if (version === OasVersion.Version2) {
124
- return OasMajorVersion.Version2;
125
- } else {
126
- return OasMajorVersion.Version3;
127
- }
128
- }
@@ -0,0 +1,58 @@
1
+ import {
2
+ Oas3Rule,
3
+ Oas3Preprocessor,
4
+ Oas2Rule,
5
+ Oas2Preprocessor,
6
+ } from './visitors';
7
+
8
+ export type RuleSet<T> = Record<string, T>;
9
+
10
+ export enum OasVersion {
11
+ Version2 = 'oas2',
12
+ Version3_0 = 'oas3_0',
13
+ Version3_1 = 'oas3_1',
14
+ }
15
+
16
+ export enum OasMajorVersion {
17
+ Version2 = 'oas2',
18
+ Version3 = 'oas3',
19
+ }
20
+
21
+ export type Oas3RuleSet = Record<string, Oas3Rule>;
22
+ export type Oas2RuleSet = Record<string, Oas2Rule>;
23
+ export type Oas3PreprocessorsSet = Record<string, Oas3Preprocessor>;
24
+ export type Oas2PreprocessorsSet = Record<string, Oas2Preprocessor>;
25
+ export type Oas3DecoratorsSet = Record<string, Oas3Preprocessor>;
26
+ export type Oas2DecoratorsSet = Record<string, Oas2Preprocessor>;
27
+
28
+ export function detectOpenAPI(root: any): OasVersion {
29
+ if (typeof root !== 'object') {
30
+ throw new Error(`Document must be JSON object, got ${typeof root}`);
31
+ }
32
+
33
+ if (!(root.openapi || root.swagger)) {
34
+ throw new Error('This doesn’t look like an OpenAPI document.\n');
35
+ }
36
+
37
+ if (root.openapi && root.openapi.startsWith('3.0')) {
38
+ return OasVersion.Version3_0;
39
+ }
40
+
41
+ if (root.openapi && root.openapi.startsWith('3.1')) {
42
+ return OasVersion.Version3_1;
43
+ }
44
+
45
+ if (root.swagger && root.swagger === '2.0') {
46
+ return OasVersion.Version2;
47
+ }
48
+
49
+ throw new Error(`Unsupported OpenAPI Version: ${root.openapi || root.swagger}`);
50
+ }
51
+
52
+ export function openAPIMajor(version: OasVersion): OasMajorVersion {
53
+ if (version === OasVersion.Version2) {
54
+ return OasMajorVersion.Version2;
55
+ } else {
56
+ return OasMajorVersion.Version3;
57
+ }
58
+ }
package/src/resolve.ts CHANGED
@@ -4,7 +4,7 @@ import * as url from 'url';
4
4
  import * as yaml from 'js-yaml';
5
5
  import { OasRef } from './typings/openapi';
6
6
  import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl } from './ref-utils';
7
- import { safeLoad as safeLoadToAst, YAMLNode, Kind } from 'yaml-ast-parser';
7
+ import type { YAMLNode, LoadOptions } from 'yaml-ast-parser';
8
8
  import { NormalizedNodeType, isNamedType } from './types';
9
9
  import { readFileFromUrl } from './utils';
10
10
  import { ResolveConfig } from './config/config';
@@ -17,14 +17,15 @@ export class Source {
17
17
  private _ast: YAMLNode | undefined;
18
18
  private _lines: string[] | undefined;
19
19
 
20
- getAst() {
20
+ // pass safeLoad as argument to separate it from browser bundle
21
+ getAst(safeLoad: (input: string, options?: LoadOptions | undefined) => YAMLNode) {
21
22
  if (this._ast === undefined) {
22
- this._ast = safeLoadToAst(this.body, { filename: this.absoluteRef }) ?? undefined;
23
+ this._ast = safeLoad(this.body, { filename: this.absoluteRef }) ?? undefined;
23
24
 
24
25
  // fix ast representation of file with newlines only
25
26
  if (
26
27
  this._ast &&
27
- this._ast.kind === Kind.SCALAR &&
28
+ this._ast.kind === 0 && // KIND.scalar = 0
28
29
  this._ast.value === '' &&
29
30
  this._ast.startPosition !== 1
30
31
  ) {
@@ -73,6 +74,18 @@ export type Document = {
73
74
  parsed: any;
74
75
  };
75
76
 
77
+ export function makeDocumentFromString(sourceString: string, absoluteRef: string) {
78
+ const source = new Source(absoluteRef, sourceString);
79
+ try {
80
+ return {
81
+ source,
82
+ parsed: yaml.safeLoad(sourceString, { filename: absoluteRef }),
83
+ };
84
+ } catch (e) {
85
+ throw new YamlParseError(e, source);
86
+ }
87
+ }
88
+
76
89
  export class BaseResolver {
77
90
  cache: Map<string, Promise<Document | ResolveError>> = new Map();
78
91
 
@@ -0,0 +1,10 @@
1
+ import { LintConfig, RuleConfig } from "../../config/config";
2
+ import { defaultPlugin } from '../../config/builtIn';
3
+
4
+ export function makeConfig(rules: Record<string, RuleConfig>) {
5
+ return new LintConfig({
6
+ plugins: [defaultPlugin],
7
+ extends: [],
8
+ rules
9
+ });
10
+ }