@redocly/openapi-core 1.16.0 → 1.17.0

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.
@@ -4,7 +4,7 @@ import { RedoclyClient } from '../redocly';
4
4
  import { isEmptyObject } from '../utils';
5
5
  import { parseYaml } from '../js-yaml';
6
6
  import { Config } from './config';
7
- import { ConfigValidationError, transformConfig } from './utils';
7
+ import { ConfigValidationError, transformConfig, deepCloneMapWithJSON } from './utils';
8
8
  import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers';
9
9
  import { bundleConfig } from '../bundle';
10
10
  import { BaseResolver } from '../resolve';
@@ -80,10 +80,12 @@ async function addConfigMetadata({
80
80
  });
81
81
  }
82
82
 
83
- export type RawConfigProcessor = (
84
- rawConfig: Document,
85
- resolvedRefMap: ResolvedRefMap
86
- ) => void | Promise<void>;
83
+ export type RawConfigProcessor = (params: {
84
+ document: Document;
85
+ resolvedRefMap: ResolvedRefMap;
86
+ config: Config;
87
+ parsed: Document['parsed'];
88
+ }) => void | Promise<void>;
87
89
 
88
90
  export async function loadConfig(
89
91
  options: {
@@ -103,12 +105,16 @@ export async function loadConfig(
103
105
  region,
104
106
  externalRefResolver,
105
107
  } = options;
106
- const rawConfig = await getConfig({ configPath, processRawConfig, externalRefResolver });
108
+
109
+ const { rawConfig, document, parsed, resolvedRefMap } = await getConfig({
110
+ configPath,
111
+ externalRefResolver,
112
+ });
107
113
 
108
114
  const redoclyClient = isBrowser ? undefined : new RedoclyClient();
109
115
  const tokens = redoclyClient && redoclyClient.hasTokens() ? redoclyClient.getAllTokens() : [];
110
116
 
111
- return addConfigMetadata({
117
+ const config = await addConfigMetadata({
112
118
  rawConfig,
113
119
  customExtends,
114
120
  configPath,
@@ -117,6 +123,24 @@ export async function loadConfig(
117
123
  region,
118
124
  externalRefResolver,
119
125
  });
126
+
127
+ if (document && parsed && resolvedRefMap && typeof processRawConfig === 'function') {
128
+ try {
129
+ await processRawConfig({
130
+ document,
131
+ resolvedRefMap,
132
+ config,
133
+ parsed,
134
+ });
135
+ } catch (e) {
136
+ if (e instanceof ConfigValidationError) {
137
+ throw e;
138
+ }
139
+ throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
140
+ }
141
+ }
142
+
143
+ return config;
120
144
  }
121
145
 
122
146
  export const CONFIG_FILE_NAMES = ['redocly.yaml', 'redocly.yml', '.redocly.yaml', '.redocly.yml'];
@@ -139,31 +163,33 @@ export function findConfig(dir?: string): string | undefined {
139
163
  export async function getConfig(
140
164
  options: {
141
165
  configPath?: string;
142
- processRawConfig?: RawConfigProcessor;
143
166
  externalRefResolver?: BaseResolver;
144
167
  } = {}
145
- ): Promise<RawConfig> {
146
- const {
147
- configPath = findConfig(),
148
- processRawConfig,
149
- externalRefResolver = new BaseResolver(),
150
- } = options;
151
- if (!configPath) return {};
168
+ ): Promise<{
169
+ rawConfig: RawConfig;
170
+ document?: Document;
171
+ parsed?: Document['parsed'];
172
+ resolvedRefMap?: ResolvedRefMap;
173
+ }> {
174
+ const { configPath = findConfig(), externalRefResolver = new BaseResolver() } = options;
175
+ if (!configPath) return { rawConfig: {} };
152
176
 
153
177
  try {
154
178
  const { document, resolvedRefMap } = await resolveConfigFileAndRefs({
155
179
  configPath,
156
180
  externalRefResolver,
157
181
  });
158
- if (typeof processRawConfig === 'function') {
159
- await processRawConfig(document, resolvedRefMap);
160
- }
161
- const bundledConfig = await bundleConfig(document, resolvedRefMap);
162
- return transformConfig(bundledConfig);
182
+
183
+ const bundledRefMap = deepCloneMapWithJSON(resolvedRefMap);
184
+ const parsed = await bundleConfig(JSON.parse(JSON.stringify(document)), bundledRefMap);
185
+
186
+ return {
187
+ rawConfig: transformConfig(parsed),
188
+ document,
189
+ parsed,
190
+ resolvedRefMap,
191
+ };
163
192
  } catch (e) {
164
- if (e instanceof ConfigValidationError) {
165
- throw e;
166
- }
167
193
  throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
168
194
  }
169
195
  }
@@ -364,3 +364,7 @@ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
364
364
  }
365
365
 
366
366
  export class ConfigValidationError extends Error {}
367
+
368
+ export function deepCloneMapWithJSON<K, V>(originalMap: Map<K, V>): Map<K, V> {
369
+ return new Map(JSON.parse(JSON.stringify([...originalMap])));
370
+ }
package/src/lint.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { BaseResolver, resolveDocument, makeDocumentFromString } from './resolve';
2
2
  import { normalizeVisitors } from './visitors';
3
3
  import { walkDocument } from './walk';
4
- import { StyleguideConfig, Config, initRules, defaultPlugin, resolvePlugins } from './config';
4
+ import { StyleguideConfig, Config, initRules } from './config';
5
5
  import { normalizeTypes } from './types';
6
6
  import { releaseAjvInstance } from './rules/ajv';
7
7
  import { SpecVersion, getMajorSpecVersion, detectSpec, getTypes } from './oas-types';
8
- import { ConfigTypes } from './types/redocly-yaml';
8
+ import { createConfigTypes } from './types/redocly-yaml';
9
9
  import { Spec } from './rules/common/spec';
10
10
  import { NoUnresolvedRefs } from './rules/no-unresolved-refs';
11
11
 
@@ -13,6 +13,7 @@ import type { Document, ResolvedRefMap } from './resolve';
13
13
  import type { ProblemSeverity, WalkContext } from './walk';
14
14
  import type { NodeType } from './types';
15
15
  import type { NestedVisitObject, Oas3Visitor, RuleInstanceConfig } from './visitors';
16
+ import { rootRedoclyConfigSchema } from '@redocly/config';
16
17
 
17
18
  export async function lint(opts: {
18
19
  ref: string;
@@ -109,25 +110,25 @@ export async function lintDocument(opts: {
109
110
 
110
111
  export async function lintConfig(opts: {
111
112
  document: Document;
113
+ config: Config;
112
114
  resolvedRefMap?: ResolvedRefMap;
113
115
  severity?: ProblemSeverity;
114
116
  externalRefResolver?: BaseResolver;
115
117
  externalConfigTypes?: Record<string, NodeType>;
116
118
  }) {
117
- const { document, severity, externalRefResolver = new BaseResolver() } = opts;
119
+ const { document, severity, externalRefResolver = new BaseResolver(), config } = opts;
118
120
 
119
121
  const ctx: WalkContext = {
120
122
  problems: [],
121
123
  oasVersion: SpecVersion.OAS3_0,
122
124
  visitorsData: {},
123
125
  };
124
- const plugins = resolvePlugins([defaultPlugin]);
125
- const config = new StyleguideConfig({
126
- plugins,
127
- rules: { spec: 'error' },
128
- });
129
126
 
130
- const types = normalizeTypes(opts.externalConfigTypes || ConfigTypes, config);
127
+ const types = normalizeTypes(
128
+ opts.externalConfigTypes || createConfigTypes(rootRedoclyConfigSchema, config),
129
+ { doNotResolveExamples: config.styleguide.doNotResolveExamples }
130
+ );
131
+
131
132
  const rules: (RuleInstanceConfig & {
132
133
  visitor: NestedVisitObject<unknown, Oas3Visitor | Oas3Visitor[]>;
133
134
  })[] = [
@@ -170,6 +170,7 @@ const Schema: NodeType = {
170
170
  format: { type: 'string' },
171
171
  contentEncoding: { type: 'string' },
172
172
  contentMediaType: { type: 'string' },
173
+ contentSchema: 'Schema',
173
174
  default: null,
174
175
  readOnly: { type: 'boolean' },
175
176
  writeOnly: { type: 'boolean' },
@@ -5,6 +5,8 @@ import { getNodeTypesFromJSONSchema } from './json-schema-adapter';
5
5
 
6
6
  import type { NodeType } from '.';
7
7
  import type { JSONSchema } from 'json-schema-to-ts';
8
+ import { SpecVersion, getTypes } from '../oas-types';
9
+ import { Config } from '../config';
8
10
 
9
11
  const builtInCommonRules = [
10
12
  'spec',
@@ -222,8 +224,6 @@ const oas3_1NodeTypesList = [
222
224
 
223
225
  export type Oas3_1NodeType = typeof oas3_1NodeTypesList[number];
224
226
 
225
- const asyncNodeTypesList = ['Message'] as const;
226
-
227
227
  const ConfigStyleguide: NodeType = {
228
228
  properties: {
229
229
  extends: {
@@ -350,35 +350,28 @@ const Schema: NodeType = {
350
350
  additionalProperties: {},
351
351
  };
352
352
 
353
- const AssertionDefinitionSubject: NodeType = {
354
- properties: {
355
- type: {
356
- enum: [
357
- ...new Set([
358
- 'any',
359
- ...oas2NodeTypesList,
360
- ...oas3NodeTypesList,
361
- ...oas3_1NodeTypesList,
362
- ...asyncNodeTypesList,
363
- 'SpecExtension',
364
- ]),
365
- ],
366
- },
367
- property: (value: unknown) => {
368
- if (Array.isArray(value)) {
369
- return { type: 'array', items: { type: 'string' } };
370
- } else if (value === null) {
371
- return null;
372
- } else {
373
- return { type: 'string' };
374
- }
353
+ function createAssertionDefinitionSubject(nodeNames: string[]): NodeType {
354
+ return {
355
+ properties: {
356
+ type: {
357
+ enum: [...new Set(['any', ...nodeNames, 'SpecExtension'])],
358
+ },
359
+ property: (value: unknown) => {
360
+ if (Array.isArray(value)) {
361
+ return { type: 'array', items: { type: 'string' } };
362
+ } else if (value === null) {
363
+ return null;
364
+ } else {
365
+ return { type: 'string' };
366
+ }
367
+ },
368
+ filterInParentKeys: { type: 'array', items: { type: 'string' } },
369
+ filterOutParentKeys: { type: 'array', items: { type: 'string' } },
370
+ matchParentKeys: { type: 'string' },
375
371
  },
376
- filterInParentKeys: { type: 'array', items: { type: 'string' } },
377
- filterOutParentKeys: { type: 'array', items: { type: 'string' } },
378
- matchParentKeys: { type: 'string' },
379
- },
380
- required: ['type'],
381
- };
372
+ required: ['type'],
373
+ };
374
+ }
382
375
 
383
376
  const AssertionDefinitionAssertions: NodeType = {
384
377
  properties: {
@@ -1057,7 +1050,13 @@ const ConfigMockServer: NodeType = {
1057
1050
  },
1058
1051
  };
1059
1052
 
1060
- export const createConfigTypes = (extraSchemas: JSONSchema) => {
1053
+ export function createConfigTypes(extraSchemas: JSONSchema, config?: Config) {
1054
+ const nodeNames = Object.values(SpecVersion).flatMap((version) => {
1055
+ const types = config?.styleguide
1056
+ ? config.styleguide.extendTypes(getTypes(version), version)
1057
+ : getTypes(version);
1058
+ return Object.keys(types);
1059
+ });
1061
1060
  // Create types based on external schemas
1062
1061
  const nodeTypes = getNodeTypesFromJSONSchema('rootRedoclyConfigSchema', extraSchemas);
1063
1062
 
@@ -1065,9 +1064,10 @@ export const createConfigTypes = (extraSchemas: JSONSchema) => {
1065
1064
  ...CoreConfigTypes,
1066
1065
  ConfigRoot: createConfigRoot(nodeTypes), // This is the REAL config root type
1067
1066
  ConfigApisProperties: createConfigApisProperties(nodeTypes),
1067
+ AssertionDefinitionSubject: createAssertionDefinitionSubject(nodeNames),
1068
1068
  ...nodeTypes,
1069
1069
  };
1070
- };
1070
+ }
1071
1071
 
1072
1072
  const CoreConfigTypes: Record<string, NodeType> = {
1073
1073
  Assert,
@@ -1130,7 +1130,6 @@ const CoreConfigTypes: Record<string, NodeType> = {
1130
1130
  Heading,
1131
1131
  Typography,
1132
1132
  AssertionDefinitionAssertions,
1133
- AssertionDefinitionSubject,
1134
1133
  };
1135
1134
 
1136
1135
  export const ConfigTypes: Record<string, NodeType> = createConfigTypes(rootRedoclyConfigSchema);