Skip to content

Commit 86ca487

Browse files
authored
feat(ls): recognize OpenAPI 3.0.x definitions (#2056)
Refs #2030
1 parent 039cdee commit 86ca487

File tree

5 files changed

+104
-60
lines changed

5 files changed

+104
-60
lines changed

package-lock.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/apidom-ls/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
"@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.46.0",
5252
"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.46.0",
5353
"@swagger-api/apidom-parser-adapter-json": "^0.46.0",
54+
"@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^0.46.0",
55+
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^0.46.0",
5456
"@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.46.0",
5557
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^0.46.0",
5658
"@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.46.0",

packages/apidom-ls/src/parser-factory.ts

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
// @ts-ignore
2-
import * as openapi3_1Adapter from '@swagger-api/apidom-parser-adapter-openapi-json-3-1';
3-
// @ts-ignore
4-
import * as asyncapi2Adapter from '@swagger-api/apidom-parser-adapter-asyncapi-json-2';
5-
// @ts-ignore
6-
import * as openapi3_1Adapter_Yaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1';
7-
// @ts-ignore
8-
import * as asyncapi2Adapter_Yaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2';
9-
// @ts-ignore
10-
import * as adsAdapter from '@swagger-api/apidom-parser-adapter-api-design-systems-json';
11-
// @ts-ignore
12-
import * as adsAdapter_Yaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml';
13-
// @ts-ignore
14-
import * as jsonParserAdapter from '@swagger-api/apidom-parser-adapter-json';
15-
// @ts-ignore
16-
import * as yamlParserAdapter from '@swagger-api/apidom-parser-adapter-yaml-1-2';
17-
// @ts-ignore
18-
import { refractorPluginReplaceEmptyElement } from '@swagger-api/apidom-ns-asyncapi-2';
19-
import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOas } from '@swagger-api/apidom-ns-openapi-3-1';
1+
import * as openapi3_0AdapterJson from '@swagger-api/apidom-parser-adapter-openapi-json-3-0';
2+
import * as openapi3_0AdapterYaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0';
3+
import * as openapi3_1AdapterJson from '@swagger-api/apidom-parser-adapter-openapi-json-3-1';
4+
import * as openapi3_1AdapterYaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1';
5+
import * as asyncapi2AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-2';
6+
import * as asyncapi2AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2';
7+
import * as adsAdapterJson from '@swagger-api/apidom-parser-adapter-api-design-systems-json';
8+
import * as adsAdapterYaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml';
9+
import * as adapterJson from '@swagger-api/apidom-parser-adapter-json';
10+
import * as adapterYaml from '@swagger-api/apidom-parser-adapter-yaml-1-2';
11+
import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementAsyncAPI2 } from '@swagger-api/apidom-ns-asyncapi-2';
12+
import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOpenAPI3_0 } from '@swagger-api/apidom-ns-openapi-3-0';
13+
import { refractorPluginReplaceEmptyElement as refractorPluginReplaceEmptyElementOpenAPI3_1 } from '@swagger-api/apidom-ns-openapi-3-1';
2014
import { TextDocument } from 'vscode-languageserver-textdocument';
2115
import { ParseResultElement } from '@swagger-api/apidom-core';
2216

@@ -42,36 +36,62 @@ export async function parse(
4236
let result;
4337
const contentLanguage = await findNamespace(text, defaultContentLanguage);
4438
if (contentLanguage.namespace === 'asyncapi' && contentLanguage.format === 'JSON') {
45-
result = await asyncapi2Adapter.parse(text, { sourceMap: true });
39+
result = await asyncapi2AdapterJson.parse(text, { sourceMap: true });
4640
} else if (contentLanguage.namespace === 'asyncapi' && contentLanguage.format === 'YAML') {
4741
const options: Record<string, unknown> = {
4842
sourceMap: true,
4943
};
5044
if (registerPlugins) {
51-
options.refractorOpts = { plugins: [refractorPluginReplaceEmptyElement()] };
45+
options.refractorOpts = { plugins: [refractorPluginReplaceEmptyElementAsyncAPI2()] };
5246
}
53-
result = await asyncapi2Adapter_Yaml.parse(text, options);
54-
} else if (contentLanguage.namespace === 'openapi' && contentLanguage.format === 'JSON') {
55-
result = await openapi3_1Adapter.parse(text, { sourceMap: true });
56-
} else if (contentLanguage.namespace === 'openapi' && contentLanguage.format === 'YAML') {
47+
result = await asyncapi2AdapterYaml.parse(text, options);
48+
} else if (
49+
contentLanguage.namespace === 'openapi' &&
50+
contentLanguage.version?.startsWith('3.0') &&
51+
contentLanguage.format === 'JSON'
52+
) {
53+
result = await openapi3_0AdapterJson.parse(text, { sourceMap: true });
54+
} else if (
55+
contentLanguage.namespace === 'openapi' &&
56+
contentLanguage.version?.startsWith('3.0') &&
57+
contentLanguage.format === 'YAML'
58+
) {
5759
const options: Record<string, unknown> = {
5860
sourceMap: true,
5961
};
6062
if (registerPlugins) {
61-
options.refractorOpts = { plugins: [refractorPluginReplaceEmptyElementOas()] };
63+
options.refractorOpts = { plugins: [refractorPluginReplaceEmptyElementOpenAPI3_0()] };
6264
}
63-
result = await openapi3_1Adapter_Yaml.parse(text, options);
65+
result = await openapi3_0AdapterYaml.parse(text, { sourceMap: true });
66+
} else if (
67+
contentLanguage.namespace === 'openapi' &&
68+
contentLanguage.version?.startsWith('3.1') &&
69+
contentLanguage.format === 'JSON'
70+
) {
71+
result = await openapi3_1AdapterJson.parse(text, { sourceMap: true });
72+
} else if (
73+
contentLanguage.namespace === 'openapi' &&
74+
contentLanguage.version?.startsWith('3.1') &&
75+
contentLanguage.format === 'YAML'
76+
) {
77+
const options: Record<string, unknown> = {
78+
sourceMap: true,
79+
};
80+
if (registerPlugins) {
81+
options.refractorOpts = { plugins: [refractorPluginReplaceEmptyElementOpenAPI3_1()] };
82+
}
83+
result = await openapi3_1AdapterYaml.parse(text, options);
6484
} else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'JSON') {
65-
result = await adsAdapter.parse(text, { sourceMap: true });
85+
result = await adsAdapterJson.parse(text, { sourceMap: true });
6686
} else if (contentLanguage.namespace === 'ads' && contentLanguage.format === 'YAML') {
67-
result = await adsAdapter_Yaml.parse(text, { sourceMap: true });
87+
result = await adsAdapterYaml.parse(text, { sourceMap: true });
6888
} else if (contentLanguage.namespace === 'apidom' && contentLanguage.format === 'JSON') {
69-
result = await jsonParserAdapter.parse(text, { sourceMap: true });
89+
result = await adapterJson.parse(text, { sourceMap: true });
7090
} else if (contentLanguage.namespace === 'apidom' && contentLanguage.format === 'YAML') {
71-
result = await yamlParserAdapter.parse(text, { sourceMap: true });
91+
result = await adapterYaml.parse(text, { sourceMap: true });
7292
} else {
7393
// fallback
74-
result = await jsonParserAdapter.parse(text, { sourceMap: true });
94+
result = await adapterJson.parse(text, { sourceMap: true });
7595
}
7696
const { api } = result;
7797
if (api === undefined) return result;

packages/apidom-ls/src/services/validation/linter-functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { FunctionItem } from '../../apidom-language-types';
2323

2424
const root = (el: Element): Element => {
2525
let node = el;
26-
while (node.parent && !['openApi3_1', 'asyncApi2'].includes(node.parent.element)) {
26+
while (node.parent && !['openApi3_0', 'openApi3_1', 'asyncApi2'].includes(node.parent.element)) {
2727
node = node.parent;
2828
}
2929
return node.parent;

packages/apidom-ls/src/utils/utils.ts

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
// @ts-ignore
2-
import * as openapi3_1Adapter from '@swagger-api/apidom-parser-adapter-openapi-json-3-1';
3-
// @ts-ignore
4-
import * as asyncapi2Adapter from '@swagger-api/apidom-parser-adapter-asyncapi-json-2';
5-
// @ts-ignore
6-
import * as openapi3_1Adapter_Yaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1';
7-
// @ts-ignore
8-
import * as asyncapi2Adapter_Yaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2';
9-
// @ts-ignore
10-
import * as adsAdapter from '@swagger-api/apidom-parser-adapter-api-design-systems-json';
11-
// @ts-ignore
12-
import * as adsAdapter_Yaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml';
13-
// @ts-ignore
14-
import * as jsonParserAdapter from '@swagger-api/apidom-parser-adapter-json';
15-
// @ts-ignore
16-
import * as yamlParserAdapter from '@swagger-api/apidom-parser-adapter-yaml-1-2';
1+
import * as openapi3_0AdapterJson from '@swagger-api/apidom-parser-adapter-openapi-json-3-0';
2+
import * as openapi3_0AdapterYaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0';
3+
import * as openapi3_1AdapterJson from '@swagger-api/apidom-parser-adapter-openapi-json-3-1';
4+
import * as openapi3_1AdapterYaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1';
5+
import * as asyncapi2AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-2';
6+
import * as asyncapi2AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2';
7+
import * as adsAdapterJson from '@swagger-api/apidom-parser-adapter-api-design-systems-json';
8+
import * as adsAdapterYaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml';
9+
import * as adapterJson from '@swagger-api/apidom-parser-adapter-json';
10+
import * as adapterYaml from '@swagger-api/apidom-parser-adapter-yaml-1-2';
1711
import {
1812
ArrayElement,
1913
BooleanElement,
@@ -738,17 +732,17 @@ export function getText(document: TextDocument | string, trim = false): string {
738732

739733
export async function isJsonDoc(document: TextDocument | string): Promise<boolean> {
740734
const text = getText(document, true);
741-
return await jsonParserAdapter.detect(text);
735+
return await adapterJson.detect(text);
742736
}
743737

744738
export function isJsonDocSync(document: TextDocument | string): boolean {
745739
const text = getText(document, true);
746-
return jsonParserAdapter.detectionRegExp.test(text);
740+
return adapterJson.detectionRegExp.test(text);
747741
}
748742

749743
export async function isYamlDoc(document: TextDocument | string): Promise<boolean> {
750744
const text = getText(document, true);
751-
return (await yamlParserAdapter.detect(text)) && !(await isJsonDoc(document));
745+
return (await adapterYaml.detect(text)) && !(await isJsonDoc(document));
752746
}
753747

754748
export async function findNamespace(
@@ -758,43 +752,67 @@ export async function findNamespace(
758752
): Promise<ContentLanguage> {
759753
const text = getText(document, true);
760754
const json = await isJsonDoc(text);
761-
if (await asyncapi2Adapter.detect(text)) {
755+
if (await asyncapi2AdapterJson.detect(text)) {
756+
const versionMatch = text.match(asyncapi2AdapterJson.detectionRegExp);
762757
return {
763758
namespace: 'asyncapi',
759+
version: versionMatch?.groups?.version_json,
764760
format: 'JSON',
765761
};
766762
}
767-
if (await asyncapi2Adapter_Yaml.detect(text)) {
763+
if (await asyncapi2AdapterYaml.detect(text)) {
764+
const versionMatch = text.match(asyncapi2AdapterYaml.detectionRegExp);
768765
return {
769766
namespace: 'asyncapi',
767+
version: versionMatch?.groups?.version_yaml || versionMatch?.groups?.version_json,
770768
format: 'YAML',
771769
};
772770
}
773-
if (await openapi3_1Adapter.detect(text)) {
771+
if (await openapi3_0AdapterJson.detect(text)) {
772+
const versionMatch = text.match(openapi3_0AdapterYaml.detectionRegExp);
774773
return {
775774
namespace: 'openapi',
775+
version: versionMatch?.groups?.version_json,
776776
format: 'JSON',
777777
};
778778
}
779-
if (await openapi3_1Adapter_Yaml.detect(text)) {
779+
if (await openapi3_0AdapterYaml.detect(text)) {
780+
const versionMatch = text.match(openapi3_0AdapterYaml.detectionRegExp);
780781
return {
781782
namespace: 'openapi',
783+
version: versionMatch?.groups?.version_yaml || versionMatch?.groups?.version_json,
782784
format: 'YAML',
783785
};
784786
}
785-
if (await adsAdapter.detect(text)) {
787+
if (await openapi3_1AdapterJson.detect(text)) {
788+
const versionMatch = text.match(openapi3_1AdapterYaml.detectionRegExp);
789+
return {
790+
namespace: 'openapi',
791+
version: versionMatch?.groups?.version_json,
792+
format: 'JSON',
793+
};
794+
}
795+
if (await openapi3_1AdapterYaml.detect(text)) {
796+
const versionMatch = text.match(openapi3_1AdapterYaml.detectionRegExp);
797+
return {
798+
namespace: 'openapi',
799+
version: versionMatch?.groups?.version_yaml || versionMatch?.groups?.version_json,
800+
format: 'YAML',
801+
};
802+
}
803+
if (await adsAdapterJson.detect(text)) {
786804
return {
787805
namespace: 'ads',
788806
format: 'JSON',
789807
};
790808
}
791-
if (await adsAdapter_Yaml.detect(text)) {
809+
if (await adsAdapterYaml.detect(text)) {
792810
return {
793811
namespace: 'ads',
794812
format: 'YAML',
795813
};
796814
}
797-
if (await jsonParserAdapter.detect(text)) {
815+
if (await adapterJson.detect(text)) {
798816
return defaultContentLanguage
799817
? {
800818
namespace: defaultContentLanguage.namespace,
@@ -806,7 +824,7 @@ export async function findNamespace(
806824
format: 'JSON',
807825
};
808826
}
809-
if (await yamlParserAdapter.detect(text)) {
827+
if (await adapterYaml.detect(text)) {
810828
return defaultContentLanguage
811829
? {
812830
namespace: defaultContentLanguage.namespace,

0 commit comments

Comments
 (0)