Skip to content

Commit

Permalink
feat(reference): change parsers from stamps to TypeScript classes (#4102
Browse files Browse the repository at this point in the history
)

Refs #3481

BREAKING CHANGE: parsers from apidom-reference package became a class and requires to be instantiated with new operator.
  • Loading branch information
char0n committed May 14, 2024
1 parent 468259e commit e3d195d
Show file tree
Hide file tree
Showing 40 changed files with 1,110 additions and 981 deletions.
62 changes: 31 additions & 31 deletions packages/apidom-reference/src/configuration/saturated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import OpenApi3_0ResolveStrategy from '../resolve/strategies/openapi-3-0';
import OpenApi3_1ResolveStrategy from '../resolve/strategies/openapi-3-1';
import AsyncApi2ResolveStrategy from '../resolve/strategies/asyncapi-2';
import ApiDOMResolveStrategy from '../resolve/strategies/apidom';
import ApiDesignSystemsJsonParser from '../parse/parsers/api-design-systems-json';
import ApiDesignSystemsYamlParser from '../parse/parsers/api-design-systems-yaml';
import OpenApiJson2Parser from '../parse/parsers/openapi-json-2';
import OpenApiYaml2Parser from '../parse/parsers/openapi-yaml-2';
import OpenApiJson3_0Parser from '../parse/parsers/openapi-json-3-0';
import OpenApiYaml3_0Parser from '../parse/parsers/openapi-yaml-3-0';
import OpenApiJson3_1Parser from '../parse/parsers/openapi-json-3-1';
import OpenApiYaml3_1Parser from '../parse/parsers/openapi-yaml-3-1';
import AsyncApiJson2Parser from '../parse/parsers/asyncapi-json-2';
import AsyncApiYaml2Parser from '../parse/parsers/asyncapi-yaml-2';
import WorkflowsJson1Parser from '../parse/parsers/workflows-json-1';
import WorkflowsYaml1Parser from '../parse/parsers/workflows-yaml-1';
import ApiDOMJsonParser from '../parse/parsers/apidom-json';
import JsonParser from '../parse/parsers/json';
import YamlParser from '../parse/parsers/yaml-1-2';
import APIDesignSystemsJSONParser from '../parse/parsers/api-design-systems-json';
import APIDesignSystemsYAMLParser from '../parse/parsers/api-design-systems-yaml';
import OpenAPIJSON2Parser from '../parse/parsers/openapi-json-2';
import OpenAPIYAML2Parser from '../parse/parsers/openapi-yaml-2';
import OpenAPIJSON3_0Parser from '../parse/parsers/openapi-json-3-0';
import OpenAPIYAML3_0Parser from '../parse/parsers/openapi-yaml-3-0';
import OpenAPIJSON3_1Parser from '../parse/parsers/openapi-json-3-1';
import OpenAPIYAML3_1Parser from '../parse/parsers/openapi-yaml-3-1';
import AsyncAPIJSON2Parser from '../parse/parsers/asyncapi-json-2';
import AsyncAPIYAML2Parser from '../parse/parsers/asyncapi-yaml-2';
import WorkflowsJSON1Parser from '../parse/parsers/workflows-json-1';
import WorkflowsYAML1Parser from '../parse/parsers/workflows-yaml-1';
import APIDOMJSONParser from '../parse/parsers/apidom-json';
import JSONParser from '../parse/parsers/json';
import YAMLParser from '../parse/parsers/yaml-1-2';
import BinaryParser from '../parse/parsers/binary/index-node';
import ApiDOMDereferenceStrategy from '../dereference/strategies/apidom';
import OpenApi2DereferenceStrategy from '../dereference/strategies/openapi-2';
Expand All @@ -30,22 +30,22 @@ import OpenApi3_1BundleStrategy from '../bundle/strategies/openapi-3-1';
import { options } from '../index';

options.parse.parsers = [
OpenApiJson2Parser({ allowEmpty: true, sourceMap: false }),
OpenApiYaml2Parser({ allowEmpty: true, sourceMap: false }),
OpenApiJson3_0Parser({ allowEmpty: true, sourceMap: false }),
OpenApiYaml3_0Parser({ allowEmpty: true, sourceMap: false }),
OpenApiJson3_1Parser({ allowEmpty: true, sourceMap: false }),
OpenApiYaml3_1Parser({ allowEmpty: true, sourceMap: false }),
AsyncApiJson2Parser({ allowEmpty: true, sourceMap: false }),
AsyncApiYaml2Parser({ allowEmpty: true, sourceMap: false }),
WorkflowsJson1Parser({ allowEmpty: true, sourceMap: false }),
WorkflowsYaml1Parser({ allowEmpty: true, sourceMap: false }),
ApiDesignSystemsJsonParser({ allowEmpty: true, sourceMap: false }),
ApiDesignSystemsYamlParser({ allowEmpty: true, sourceMap: false }),
ApiDOMJsonParser({ allowEmpty: true, sourceMap: false }),
JsonParser({ allowEmpty: true, sourceMap: false }),
YamlParser({ allowEmpty: true, sourceMap: false }),
BinaryParser({ allowEmpty: true }),
new OpenAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
new OpenAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
new OpenAPIJSON3_0Parser({ allowEmpty: true, sourceMap: false }),
new OpenAPIYAML3_0Parser({ allowEmpty: true, sourceMap: false }),
new OpenAPIJSON3_1Parser({ allowEmpty: true, sourceMap: false }),
new OpenAPIYAML3_1Parser({ allowEmpty: true, sourceMap: false }),
new AsyncAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
new AsyncAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
new WorkflowsJSON1Parser({ allowEmpty: true, sourceMap: false }),
new WorkflowsYAML1Parser({ allowEmpty: true, sourceMap: false }),
new APIDesignSystemsJSONParser({ allowEmpty: true, sourceMap: false }),
new APIDesignSystemsYAMLParser({ allowEmpty: true, sourceMap: false }),
new APIDOMJSONParser({ allowEmpty: true, sourceMap: false }),
new JSONParser({ allowEmpty: true, sourceMap: false }),
new YAMLParser({ allowEmpty: true, sourceMap: false }),
new BinaryParser({ allowEmpty: true }),
];

options.resolve.resolvers = [
Expand Down
5 changes: 3 additions & 2 deletions packages/apidom-reference/src/parse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { ParseResultElement } from '@swagger-api/apidom-core';
import * as url from '../util/url';
import File from '../File';
import * as plugins from '../util/plugins';
import { ReferenceOptions as IReferenceOptions, Parser as IParser } from '../types';
import { ReferenceOptions as IReferenceOptions } from '../types';
import Parser from './parsers/Parser';
import ParseError from '../errors/ParseError';
import UnmatchedResolverError from '../errors/UnmatchedResolverError';
import { readFile } from '../resolve/util';
Expand All @@ -18,7 +19,7 @@ const parseFile = async (file: File, options: IReferenceOptions): Promise<ParseR
return Object.assign(clonedParser, options.parse.parserOpts);
});

const parsers: IParser[] = await plugins.filter('canParse', [file, options], optsBoundParsers);
const parsers: Parser[] = await plugins.filter('canParse', [file, options], optsBoundParsers);

// we couldn't find any parser for this File
if (isEmpty(parsers)) {
Expand Down
92 changes: 47 additions & 45 deletions packages/apidom-reference/src/parse/parsers/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
import stampit from 'stampit';
import { ParseResultElement } from '@swagger-api/apidom-core';
import { NotImplementedError } from '@swagger-api/apidom-error';

import { Parser as IParser } from '../../types';

const Parser: stampit.Stamp<IParser> = stampit({
props: {
name: '',
/**
* Whether to allow "empty" files. This includes zero-byte files.
*/
allowEmpty: true,

/**
* Whether to generate source map during parsing.
*/
sourceMap: false,
/**
* List of supported file extensions.
*/
fileExtensions: [],
/**
* List of supported media types.
*/
mediaTypes: [],
},
init(
this: IParser,
{
allowEmpty = this.allowEmpty,
sourceMap = this.sourceMap,
fileExtensions = this.fileExtensions,
mediaTypes = this.mediaTypes,
} = {},
) {

import File from '../../File';

export interface ParserOptions {
readonly name: string;
readonly allowEmpty?: boolean;
readonly sourceMap?: boolean;
readonly fileExtensions?: string[];
readonly mediaTypes?: string[];
}

abstract class Parser {
public readonly name: string;

/**
* Whether to allow "empty" files. This includes zero-byte files.
*/
public allowEmpty: boolean;

/**
* Whether to generate source map during parsing.
*/
public sourceMap: boolean;

/**
* List of supported file extensions.
*/
public fileExtensions: string[];

/**
* List of supported media types.
*/
public mediaTypes: string[];

constructor({
name,
allowEmpty = true,
sourceMap = false,
fileExtensions = [],
mediaTypes = [],
}: ParserOptions) {
this.name = name;
this.allowEmpty = allowEmpty;
this.sourceMap = sourceMap;
this.fileExtensions = fileExtensions;
this.mediaTypes = mediaTypes;
},
methods: {
async canParse(): Promise<boolean> {
throw new NotImplementedError('canParse method in Parser stamp is not yet implemented.');
},
async parse(): Promise<ParseResultElement> {
throw new NotImplementedError('parse method in Parser stamp is not yet implemented.');
},
},
});
}

abstract canParse(file: File): boolean | Promise<boolean>;
abstract parse(file: File): ParseResultElement | Promise<ParseResultElement>;
}

export default Parser;
Original file line number Diff line number Diff line change
@@ -1,47 +1,51 @@
import stampit from 'stampit';
import { pick } from 'ramda';
import { ParseResultElement } from '@swagger-api/apidom-core';
import {
parse,
mediaTypes,
mediaTypes as ADSMediaTypes,
detect,
} from '@swagger-api/apidom-parser-adapter-api-design-systems-json';

import ParserError from '../../../errors/ParserError';
import { Parser as IParser } from '../../../types';
import Parser from '../Parser';
import Parser, { ParserOptions } from '../Parser';
import File from '../../../File';

const ApiDesignSystemsJsonParser: stampit.Stamp<IParser> = stampit(Parser, {
props: {
name: 'api-design-systems-json',
fileExtensions: ['.json'],
mediaTypes,
},
methods: {
async canParse(file: File): Promise<boolean> {
const hasSupportedFileExtension =
this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);

if (!hasSupportedFileExtension) return false;
if (hasSupportedMediaType) return true;
if (!hasSupportedMediaType) {
return detect(file.toString());
}
return false;
},
async parse(file: File): Promise<ParseResultElement> {
const source = file.toString();

try {
const parserOpts = pick(['sourceMap', 'syntacticAnalysis', 'refractorOpts'], this);
return await parse(source, parserOpts);
} catch (error: any) {
throw new ParserError(`Error parsing "${file.uri}"`, { cause: error });
}
},
},
});

export default ApiDesignSystemsJsonParser;
export interface APIDesignSystemsJSONParserOptions extends Omit<ParserOptions, 'name'> {}

class APIDesignSystemsJSONParser extends Parser {
public syntacticAnalysis?: 'direct' | 'indirect';

public refractorOpts!: object;

constructor(options?: APIDesignSystemsJSONParserOptions) {
const { fileExtensions = ['.json'], mediaTypes = ADSMediaTypes, ...rest } = options ?? {};

super({ ...rest, name: 'api-design-systems-json', fileExtensions, mediaTypes });
}

async canParse(file: File): Promise<boolean> {
const hasSupportedFileExtension =
this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);

if (!hasSupportedFileExtension) return false;
if (hasSupportedMediaType) return true;
if (!hasSupportedMediaType) {
return detect(file.toString());
}
return false;
}

async parse(file: File): Promise<ParseResultElement> {
const source = file.toString();

try {
const parserOpts = pick(['sourceMap', 'syntacticAnalysis', 'refractorOpts'], this);
return await parse(source, parserOpts);
} catch (error: unknown) {
throw new ParserError(`Error parsing "${file.uri}"`, { cause: error });
}
}
}

export default APIDesignSystemsJSONParser;
Original file line number Diff line number Diff line change
@@ -1,47 +1,53 @@
import stampit from 'stampit';
import { pick } from 'ramda';
import { ParseResultElement } from '@swagger-api/apidom-core';
import {
parse,
mediaTypes,
mediaTypes as ADSMediaTypes,
detect,
} from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml';

import ParserError from '../../../errors/ParserError';
import { Parser as IParser } from '../../../types';
import Parser from '../Parser';
import Parser, { ParserOptions } from '../Parser';
import File from '../../../File';

const ApiDesignSystemsYamlParser: stampit.Stamp<IParser> = stampit(Parser, {
props: {
name: 'api-design-systems-yaml',
fileExtensions: ['.yaml', '.yml'],
mediaTypes,
},
methods: {
async canParse(file: File): Promise<boolean> {
const hasSupportedFileExtension =
this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);

if (!hasSupportedFileExtension) return false;
if (hasSupportedMediaType) return true;
if (!hasSupportedMediaType) {
return detect(file.toString());
}
return false;
},
async parse(file: File): Promise<ParseResultElement> {
const source = file.toString();

try {
const parserOpts = pick(['sourceMap', 'refractorOpts'], this);
return await parse(source, parserOpts);
} catch (error: any) {
throw new ParserError(`Error parsing "${file.uri}"`, { cause: error });
}
},
},
});

export default ApiDesignSystemsYamlParser;
export interface APIDesignSystemsYAMLParserOptions extends Omit<ParserOptions, 'name'> {}

class APIDesignSystemsYAMLParser extends Parser {
public refractorOpts!: object;

constructor(options?: APIDesignSystemsYAMLParserOptions) {
const {
fileExtensions = ['.yaml', '.yml'],
mediaTypes = ADSMediaTypes,
...rest
} = options ?? {};

super({ ...rest, name: 'api-design-systems-yaml', fileExtensions, mediaTypes });
}

async canParse(file: File): Promise<boolean> {
const hasSupportedFileExtension =
this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);

if (!hasSupportedFileExtension) return false;
if (hasSupportedMediaType) return true;
if (!hasSupportedMediaType) {
return detect(file.toString());
}
return false;
}

async parse(file: File): Promise<ParseResultElement> {
const source = file.toString();

try {
const parserOpts = pick(['sourceMap', 'refractorOpts'], this);
return await parse(source, parserOpts);
} catch (error: unknown) {
throw new ParserError(`Error parsing "${file.uri}"`, { cause: error });
}
}
}

export default APIDesignSystemsYAMLParser;

0 comments on commit e3d195d

Please sign in to comment.