Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/cli/src/commands/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ export async function handleStats({ argv, config, collectSpecData }: CommandArgs
const { bundle: document } = await bundle({ config, ref: path });
collectSpecData?.(document.parsed);
const specVersion = detectSpec(document.parsed);
const types = normalizeTypes(config.extendTypes(getTypes(specVersion), specVersion), config);
const types = normalizeTypes(
config.extendTypes(await getTypes(specVersion), specVersion),
config
);

const startedAt = performance.now();
const ctx: WalkContext = {
Expand Down
5 changes: 5 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"require": "./lib/index.js",
"types": "./lib/index.d.ts"
},
"./resolve": {
"import": "./lib/resolve.js",
"require": "./lib/resolve.js",
"types": "./lib/resolve.d.ts"
},
"./lib/config/config": {
"import": "./lib/config/index.js",
"require": "./lib/config/index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ describe('lint', () => {
it('lintConfig should alternate its behavior when supplied externalConfigTypes', async () => {
const config = await createConfig(testPortalConfigContent);
const results = await lintConfig({
externalConfigTypes: createConfigTypes(
externalConfigTypes: await createConfigTypes(
{
type: 'object',
properties: {
Expand Down
18 changes: 12 additions & 6 deletions packages/core/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import { isTruthy } from './utils.js';
import { dequal } from './utils/dequal.js';
import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components.js';
import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components.js';
import { NormalizedConfigTypes } from './types/redocly-yaml.js';
import { getNormalizedConfigTypes } from './types/redocly-yaml.js';
import { type Config } from './config/config.js';
import { configBundlerVisitor, pluginsCollectorVisitor } from './config/visitors.js';
import { getConfigBundlerVisitor, getPluginsCollectorVisitor } from './config/visitors.js';
import { CONFIG_BUNDLER_VISITOR_ID, PLUGINS_COLLECTOR_VISITOR_ID } from './config/constants.js';

import type { ConfigBundlerVisitorData, PluginsCollectorVisitorData } from './config/visitors.js';
Expand All @@ -34,7 +34,7 @@ export type CoreBundleOptions = {
keepUrlRefs?: boolean;
};

export function collectConfigPlugins(
export async function collectConfigPlugins(
document: Document,
resolvedRefMap: ResolvedRefMap,
rootConfigDir: string
Expand All @@ -49,6 +49,9 @@ export function collectConfigPlugins(
},
};

const pluginsCollectorVisitor = await getPluginsCollectorVisitor();
const NormalizedConfigTypes = await getNormalizedConfigTypes();

walkDocument({
document,
rootType: NormalizedConfigTypes.ConfigRoot,
Expand All @@ -60,11 +63,11 @@ export function collectConfigPlugins(
return visitorsData.plugins;
}

export function bundleConfig(
export async function bundleConfig(
document: Document,
resolvedRefMap: ResolvedRefMap,
plugins: Plugin[]
): ResolvedConfig {
): Promise<ResolvedConfig> {
const visitorsData: ConfigBundlerVisitorData = { plugins };
const ctx: BundleContext = {
problems: [],
Expand All @@ -75,6 +78,9 @@ export function bundleConfig(
},
};

const configBundlerVisitor = await getConfigBundlerVisitor();
const NormalizedConfigTypes = await getNormalizedConfigTypes();

walkDocument({
document,
rootType: NormalizedConfigTypes.ConfigRoot,
Expand Down Expand Up @@ -167,7 +173,7 @@ export async function bundleDocument(opts: {
const specMajorVersion = getMajorSpecVersion(specVersion);
const rules = config.getRulesForSpecVersion(specMajorVersion);
const types = normalizeTypes(
config.extendTypes(customTypes ?? getTypes(specVersion), specVersion),
config.extendTypes(customTypes ?? (await getTypes(specVersion)), specVersion),
config
);

Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/config/config-resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { isBrowser } from '../env.js';
import { colorize, logger } from '../logger.js';
import { asserts, buildAssertCustomFunction } from '../rules/common/assertions/asserts.js';
import { NormalizedConfigTypes } from '../types/redocly-yaml.js';
import { getNormalizedConfigTypes } from '../types/redocly-yaml.js';
import { bundleConfig, collectConfigPlugins } from '../bundle.js';
import { CONFIG_FILE_NAME, DEFAULT_CONFIG, DEFAULT_PROJECT_PLUGIN_PATHS } from './constants.js';

Expand Down Expand Up @@ -77,6 +77,9 @@ export async function resolveConfig({
source: new Source(configPath ?? '', JSON.stringify(config)),
parsed: config,
};

const NormalizedConfigTypes = await getNormalizedConfigTypes();

const resolvedRefMap = await resolveDocument({
rootDocument,
rootType: NormalizedConfigTypes.ConfigRoot,
Expand All @@ -96,15 +99,15 @@ export async function resolveConfig({
resolvedPlugins = [...instantiatedPlugins, defaultPlugin];
} else {
rootConfigDir = path.dirname(configPath ?? '');
pluginsOrPaths = collectConfigPlugins(rootDocument, resolvedRefMap, rootConfigDir);
pluginsOrPaths = await collectConfigPlugins(rootDocument, resolvedRefMap, rootConfigDir);
const plugins = await resolvePlugins(
pluginsOrPaths.map((p) => (isPluginResolveInfo(p) ? p.absolutePath : p)),
rootConfigDir
);
resolvedPlugins = [...plugins, defaultPlugin];
}

const bundledConfig = bundleConfig(
const bundledConfig = await bundleConfig(
rootDocument,
deepCloneMapWithJSON(resolvedRefMap),
resolvedPlugins
Expand Down
153 changes: 85 additions & 68 deletions packages/core/src/config/visitors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NormalizedConfigTypes } from '../types/redocly-yaml.js';
import { getNormalizedConfigTypes } from '../types/redocly-yaml.js';
import { normalizeVisitors } from '../visitors.js';
import { replaceRef } from '../ref-utils.js';
import { bundleExtends } from './bundle-extends.js';
Expand Down Expand Up @@ -31,38 +31,47 @@ function collectorHandleNode(node: unknown, ctx: UserContext) {
}
}

export const pluginsCollectorVisitor = normalizeVisitors(
[
{
severity: 'error',
ruleId: PLUGINS_COLLECTOR_VISITOR_ID,
visitor: {
ref: {},
ConfigGovernance: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
ConfigApisProperties: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
'rootRedoclyConfigSchema.scorecard.levels_items': {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
ConfigRoot: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
// Cache for visitors
let _pluginsCollectorVisitor: any = null;

export async function getPluginsCollectorVisitor() {
if (!_pluginsCollectorVisitor) {
const NormalizedConfigTypes = await getNormalizedConfigTypes();
_pluginsCollectorVisitor = normalizeVisitors(
[
{
severity: 'error',
ruleId: PLUGINS_COLLECTOR_VISITOR_ID,
visitor: {
ref: {},
ConfigGovernance: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
ConfigApisProperties: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
'rootRedoclyConfigSchema.scorecard.levels_items': {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
ConfigRoot: {
leave(node: unknown, ctx: UserContext) {
collectorHandleNode(node, ctx);
},
},
} as any,
},
},
},
],
NormalizedConfigTypes
);
],
NormalizedConfigTypes
);
}
return _pluginsCollectorVisitor;
}

export type ConfigBundlerVisitorData = {
plugins: Plugin[];
Expand All @@ -77,40 +86,48 @@ function bundlerHandleNode(node: unknown, ctx: UserContext) {
}
}

export const configBundlerVisitor = normalizeVisitors(
[
{
severity: 'error',
ruleId: CONFIG_BUNDLER_VISITOR_ID,
visitor: {
ref: {
leave(node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) {
replaceRef(node, resolved, ctx);
},
},
ConfigGovernance: {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
},
ConfigApisProperties: {
leave(node: unknown, ctx: UserContext) {
// ignore extends from root config if defined in the current node
bundlerHandleNode(node, ctx);
},
},
'rootRedoclyConfigSchema.scorecard.levels_items': {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
},
ConfigRoot: {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
let _configBundlerVisitor: any = null;

export async function getConfigBundlerVisitor() {
if (!_configBundlerVisitor) {
const NormalizedConfigTypes = await getNormalizedConfigTypes();
_configBundlerVisitor = normalizeVisitors(
[
{
severity: 'error',
ruleId: CONFIG_BUNDLER_VISITOR_ID,
visitor: {
ref: {
leave(node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) {
replaceRef(node, resolved, ctx);
},
},
ConfigGovernance: {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
},
ConfigApisProperties: {
leave(node: unknown, ctx: UserContext) {
// ignore extends from root config if defined in the current node
bundlerHandleNode(node, ctx);
},
},
'rootRedoclyConfigSchema.scorecard.levels_items': {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
},
ConfigRoot: {
leave(node: unknown, ctx: UserContext) {
bundlerHandleNode(node, ctx);
},
},
} as any,
},
},
},
],
NormalizedConfigTypes
);
],
NormalizedConfigTypes
);
}
return _configBundlerVisitor;
}
6 changes: 5 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export { AsyncApi2Types } from './types/asyncapi2.js';
export { AsyncApi3Types } from './types/asyncapi3.js';
export { Arazzo1Types } from './types/arazzo.js';
export { Overlay1Types } from './types/overlay.js';
export { ConfigTypes, createConfigTypes } from './types/redocly-yaml.js';
export {
createConfigTypes,
getConfigTypes,
getNormalizedConfigTypes,
} from './types/redocly-yaml.js';
export { normalizeTypes, type NormalizedNodeType, type NodeType } from './types/index.js';
export { Stats } from './rules/other/stats.js';
export {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export async function lintDocument(opts: {
const specMajorVersion = getMajorSpecVersion(specVersion);
const rules = config.getRulesForSpecVersion(specMajorVersion);
const types = normalizeTypes(
config.extendTypes(customTypes ?? getTypes(specVersion), specVersion),
config.extendTypes(customTypes ?? (await getTypes(specVersion)), specVersion),
config
);

Expand Down Expand Up @@ -143,7 +143,7 @@ export async function lintConfig(opts: {
};

const types = normalizeTypes(
opts.externalConfigTypes || createConfigTypes(rootRedoclyConfigSchema, config)
opts.externalConfigTypes || (await createConfigTypes(rootRedoclyConfigSchema, config))
);

const rules: (RuleInstanceConfig & {
Expand Down
42 changes: 21 additions & 21 deletions packages/core/src/oas-types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
import { Oas2Types } from './types/oas2.js';
import { Oas3Types } from './types/oas3.js';
import { Oas3_1Types } from './types/oas3_1.js';
import { Oas3_2Types } from './types/oas3_2.js';
import { AsyncApi2Types } from './types/asyncapi2.js';
import { AsyncApi3Types } from './types/asyncapi3.js';
import { Arazzo1Types } from './types/arazzo.js';
import { Overlay1Types } from './types/overlay.js';
import { isPlainObject } from './utils.js';
import { VERSION_PATTERN } from './typings/arazzo.js';

Expand Down Expand Up @@ -47,17 +39,6 @@ export type SpecVersion = typeof specVersions[number];

export type SpecMajorVersion = 'oas2' | 'oas3' | 'async2' | 'async3' | 'arazzo1' | 'overlay1';

const typesMap = {
oas2: Oas2Types,
oas3_0: Oas3Types,
oas3_1: Oas3_1Types,
oas3_2: Oas3_2Types,
async2: AsyncApi2Types,
async3: AsyncApi3Types,
arazzo1: Arazzo1Types,
overlay1: Overlay1Types,
};

export type RuleMap<Key extends string, RuleConfig, T> = Record<
T extends 'built-in' ? Key : string,
RuleConfig
Expand Down Expand Up @@ -175,6 +156,25 @@ export function getMajorSpecVersion(version: SpecVersion): SpecMajorVersion {
}
}

export function getTypes(spec: SpecVersion) {
return typesMap[spec];
export async function getTypes(spec: SpecVersion) {
switch (spec) {
case 'oas2':
return (await import('./types/oas2.js')).Oas2Types;
case 'oas3_0':
return (await import('./types/oas3.js')).Oas3Types;
case 'oas3_1':
return (await import('./types/oas3_1.js')).Oas3_1Types;
case 'oas3_2':
return (await import('./types/oas3_2.js')).Oas3_2Types;
case 'async2':
return (await import('./types/asyncapi2.js')).AsyncApi2Types;
case 'async3':
return (await import('./types/asyncapi3.js')).AsyncApi3Types;
case 'arazzo1':
return (await import('./types/arazzo.js')).Arazzo1Types;
case 'overlay1':
return (await import('./types/overlay.js')).Overlay1Types;
default:
throw new Error(`Unsupported specification: ${spec}`);
}
}
Loading
Loading