UNPKG

@redocly/openapi-core

Version:

See https://github.com/Redocly/redocly-cli

174 lines 6.53 kB
import { rootRedoclyConfigSchema } from '@redocly/config'; import { BaseResolver, resolveDocument, makeDocumentFromString } from './resolve.js'; import { normalizeVisitors } from './visitors.js'; import { walkDocument } from './walk.js'; import { initRules } from './config/rules.js'; import { normalizeTypes } from './types/index.js'; import { releaseAjvInstance } from './rules/ajv.js'; import { getTypes } from './oas-types.js'; import { detectSpec, getMajorSpecVersion } from './detect-spec.js'; import { createConfigTypes } from './types/redocly-yaml.js'; import { createEntityTypes, ENTITY_DISCRIMINATOR_NAME } from './types/entity-yaml.js'; import { Struct } from './rules/common/struct.js'; import { NoUnresolvedRefs } from './rules/common/no-unresolved-refs.js'; import { EntityKeyValid } from './rules/catalog-entity/entity-key-valid.js'; import { isPlainObject } from './utils/is-plain-object.js'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore FIXME: remove this once we remove `theme` from the schema delete rootRedoclyConfigSchema.properties.theme; export async function lint(opts) { const { ref, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts; const document = (await externalRefResolver.resolveDocument(null, ref, true)); opts.collectSpecData?.(document.parsed); return lintDocument({ document, ...opts, externalRefResolver, }); } export async function lintFromString(opts) { const { source, absoluteRef, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts; const document = makeDocumentFromString(source, absoluteRef || '/'); return lintDocument({ document, ...opts, externalRefResolver, }); } export async function lintDocument(opts) { releaseAjvInstance(); // FIXME: preprocessors can modify nodes which are then cached to ajv-instance by absolute path const { document, customTypes, externalRefResolver, config } = opts; const specVersion = detectSpec(document.parsed); const specMajorVersion = getMajorSpecVersion(specVersion); const rules = config.getRulesForSpecVersion(specMajorVersion); const types = normalizeTypes(config.extendTypes(customTypes ?? getTypes(specVersion), specVersion), config); const ctx = { problems: [], specVersion, config, visitorsData: {}, }; const preprocessors = initRules(rules, config, 'preprocessors', specVersion); const regularRules = initRules(rules, config, 'rules', specVersion); let resolvedRefMap = await resolveDocument({ rootDocument: document, rootType: types.Root, externalRefResolver, }); if (preprocessors.length > 0) { // Make additional pass to resolve refs defined in preprocessors. walkDocument({ document, rootType: types.Root, normalizedVisitors: normalizeVisitors(preprocessors, types), resolvedRefMap, ctx, }); resolvedRefMap = await resolveDocument({ rootDocument: document, rootType: types.Root, externalRefResolver, }); } const normalizedVisitors = normalizeVisitors(regularRules, types); walkDocument({ document, rootType: types.Root, normalizedVisitors, resolvedRefMap, ctx, }); return ctx.problems.map((problem) => config.addProblemToIgnore(problem)); } export async function lintConfig(opts) { const { severity, externalRefResolver = new BaseResolver(), config } = opts; if (!config.document) { throw new Error('Config document is not set.'); } const ctx = { problems: [], specVersion: 'oas3_0', // TODO: use config-specific version config, visitorsData: {}, }; const types = normalizeTypes(opts.externalConfigTypes || createConfigTypes(rootRedoclyConfigSchema, config)); const rules = [ { severity: severity || 'error', ruleId: 'configuration struct', visitor: Struct({ severity: 'error' }), }, { severity: severity || 'error', ruleId: 'configuration no-unresolved-refs', visitor: NoUnresolvedRefs({ severity: 'error' }), }, ]; const normalizedVisitors = normalizeVisitors(rules, types); const resolvedRefMap = config.resolvedRefMap || (await resolveDocument({ rootDocument: config.document, rootType: types.ConfigRoot, externalRefResolver, })); walkDocument({ document: config.document, rootType: types.ConfigRoot, normalizedVisitors, resolvedRefMap, ctx, }); return ctx.problems; } export async function lintEntityFile(opts) { const { document, entitySchema, entityDefaultSchema, severity, externalRefResolver = new BaseResolver(), } = opts; const ctx = { problems: [], specVersion: 'entity', // FIXME: this should be proper SpecVersion visitorsData: {}, }; const entityTypes = createEntityTypes(entitySchema, entityDefaultSchema); const types = normalizeTypes(entityTypes); let rootType = types.EntityFileDefault; if (Array.isArray(document.parsed)) { rootType = types.EntityFileArray; } else if (isPlainObject(document.parsed)) { const typeValue = document.parsed[ENTITY_DISCRIMINATOR_NAME]; if (typeof typeValue === 'string' && types[typeValue]) { rootType = types[typeValue]; } } const rules = [ { severity: severity || 'error', ruleId: 'entity struct', visitor: Struct({ severity: 'error' }), }, { severity: severity || 'error', ruleId: 'entity no-unresolved-refs', visitor: NoUnresolvedRefs({ severity: 'error' }), }, { severity: severity || 'error', ruleId: 'entity key-valid', visitor: EntityKeyValid({ severity: 'error' }), }, ]; const normalizedVisitors = normalizeVisitors(rules, types); const resolvedRefMap = await resolveDocument({ rootDocument: document, rootType, externalRefResolver, }); walkDocument({ document, rootType, normalizedVisitors, resolvedRefMap, ctx, }); return ctx.problems; } //# sourceMappingURL=lint.js.map