Skip to content

Commit

Permalink
feat: implement JSON parser adapter for JSON Schema 2020-12 (#4677)
Browse files Browse the repository at this point in the history
Refs #1867
  • Loading branch information
glowcloud authored Jan 23, 2025
1 parent 1066cb6 commit d7fcb96
Show file tree
Hide file tree
Showing 23 changed files with 977 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ You can install ApiDOM packages using [npm CLI](https://docs.npmjs.com/cli):
$ npm install @swagger-api/apidom-parser-adapter-asyncapi-json-2
$ npm install @swagger-api/apidom-parser-adapter-asyncapi-yaml-2
$ npm install @swagger-api/apidom-parser-adapter-json
$ npm install @swagger-api/apidom-parser-adapter-json-schema-json-2020-12
$ npm install @swagger-api/apidom-parser-adapter-openapi-json-2
$ npm install @swagger-api/apidom-parser-adapter-openapi-json-3-0
$ npm install @swagger-api/apidom-parser-adapter-openapi-json-3-1
Expand Down
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**/*.js
/**/*.mjs
/**/*.cjs
/dist
/types
/config
/.nyc_output
/node_modules
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/src/**/*.mjs
/src/**/*.cjs
/test/**/*.mjs
/dist
/types
/NOTICE
/swagger-api-apidom-parser-adapter-json-schema-yaml-2020-12-*.tgz
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recursive": true,
"spec": "test/**/*.mjs",
"file": ["test/mocha-bootstrap.mjs"],
"ignore": ["test/perf/**/*.mjs"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
save-prefix="="
save=false
91 changes: 91 additions & 0 deletions packages/apidom-parser-adapter-json-schema-json-2020-12/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# @swagger-api/apidom-parser-adapter-json-schema-json-2020-12

`@swagger-api/apidom-parser-adapter-json-schema-json-2020-12` is a parser adapter for the [JSON Schema 2020-12](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01) in [JSON format](https://www.json.org/json-en.html).
Under the hood this adapter uses [apidom-parser-adapter-json](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-json)
to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom#base-namespace)
which is then refracted with [JSON Schema 2020-12 Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-json-schema-2020-12#refractors).

## Installation

After [prerequisites](https://github.com/swagger-api/apidom/blob/main/README.md#prerequisites) for installing this package are satisfied, you can install it
via [npm CLI](https://docs.npmjs.com/cli) by running the following command:

```sh
$ npm install @swagger-api/apidom-parser-adapter-json-schema-json-2020-12
```

## Parser adapter API

This parser adapter is fully compatible with parser adapter interface required by [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#mounting-parser-adapters)
and implements all required properties.

### mediaTypes

Defines list of media types that this parser adapter recognizes.

```js
[
'application/schema;version=2020-12',
'application/schema+json;version=2020-12',
]
```

### detect

[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-json-schema-json-2020-12/src/adapter.ts#L13) is based on a regular expression matching required JSON Schema 2020-12 symbols in JSON format.

### namespace

This adapter exposes an instance of [JSON Schema 2020-12 ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-json-schema-2020-12#json-schema-2020-12-namespace).

### parse

`parse` function consumes various options as a second argument. Here is a list of these options:

Option | Type | Default | Description
--- | --- | --- | ---
<a name="specObj"></a>`specObj` | `Object` | [Specification Object](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-json-schema-2020-12/src/refractor/specification.ts) | This specification object drives the JSON AST transformation to JSON Schema 2020-12 ApiDOM namespace.
<a name="sourceMap"></a>`sourceMap` | `Boolean` | `false` | Indicate whether to generate source maps.
<a name="refractorOpts"></a>`refractorOpts` | `Object` | `{}` | Refractor options are [passed to refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-json-schema-2020-12#refractor-plugins) during refracting phase.

All unrecognized arbitrary options will be ignored.

## Usage

This parser adapter can be used directly or indirectly via [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser).

### Direct usage

During direct usage you don't need to provide `mediaType` as the `parse` function is already pre-bound
with [supported media types](#mediatypes).

```js
import { parse, detect } from '@swagger-api/apidom-parser-adapter-json-schema-json-2020-12';

// detecting
await detect('{"$schema": "https://json-schema.org/draft/2020-12/schema"}'); // => true
await detect('test'); // => false

// parsing
const parseResult = await parse('{"$schema": "https://json-schema.org/draft/2020-12/schema"}', {
sourceMap: true,
});
```

### Indirect usage

You can omit the `mediaType` option here, but please read [Word on detect vs mediaTypes](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#word-on-detect-vs-mediatypes) before you do so.

```js
import ApiDOMParser from '@swagger-api/apidom-parser';
import * as jsonSchemaJsonAdapter from '@swagger-api/apidom-parser-adapter-json-schema-json-2020-12';

const parser = new ApiDOMParser();

parser.use(jsonSchemaJsonAdapter);

const parseResult = await parser.parse(
'{"$schema": "https://json-schema.org/draft/2020-12/schema"}',
{ mediaType: jsonSchemaJsonAdapter.mediaTypes.latest('json') },
);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"extends": "../../../../api-extractor.json",
"mainEntryPointFilePath": "../../types/adapter.d.ts"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import path from 'node:path';
import { nonMinimizeTrait, minimizeTrait } from './traits.config.js';

const browser = {
mode: 'production',
entry: ['./src/adapter.ts'],
target: 'web',
performance: {
maxEntrypointSize: 1800000,
maxAssetSize: 1800000,
},
output: {
path: path.resolve('./dist'),
filename: 'apidom-parser-adapter-json-schema-json-2020-12.browser.js',
libraryTarget: 'umd',
library: 'apidomParserAdapterJSONSchemaJSON202012',
},
resolve: {
extensions: ['.ts', '.mjs', '.js', '.json'],
fallback: {
fs: false,
path: false,
},
},
module: {
rules: [
{
test: /\.wasm$/,
loader: 'file-loader',
type: 'javascript/auto',
},
{
test: /\.(ts|js)?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
babelrc: true,
rootMode: 'upward',
},
},
},
],
},
...nonMinimizeTrait,
};

const browserMin = {
mode: 'production',
entry: ['./src/adapter.ts'],
target: 'web',
performance: {
maxEntrypointSize: 280000,
maxAssetSize: 280000,
},
output: {
path: path.resolve('./dist'),
filename: 'apidom-parser-adapter-json-schema-json-2020-12.browser.min.js',
libraryTarget: 'umd',
library: 'apidomParserAdapterJSONSchemaJSON202012',
},
resolve: {
extensions: ['.ts', '.mjs', '.js', '.json'],
fallback: {
fs: false,
path: false,
},
},
module: {
rules: [
{
test: /\.wasm$/,
loader: 'file-loader',
type: 'javascript/auto',
},
{
test: /\.(ts|js)?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
babelrc: true,
rootMode: 'upward',
},
},
},
],
},
...minimizeTrait,
};

export default [browser, browserMin];
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import webpack from 'webpack';
import TerserPlugin from 'terser-webpack-plugin';

export const nonMinimizeTrait = {
optimization: {
minimize: false,
usedExports: false,
concatenateModules: false,
},
};

export const minimizeTrait = {
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
}),
],
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
},
output: {
comments: false,
},
},
}),
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@swagger-api/apidom-parser-adapter-json-schema-json-2020-12",
"version": "1.0.0-beta.8",
"description": "Parser adapter for parsing JSON documents into JSON Schema 2020-12 namespace.",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"type": "module",
"sideEffects": false,
"unpkg": "./dist/apidom-parser-apdater-json-schema-json-2020-12.browser.min.js",
"main": "./src/adapter.cjs",
"exports": {
"types": "./types/apidom-parser-adapter-json-schema-json-2020-12.d.ts",
"import": "./src/adapter.mjs",
"require": "./src/adapter.cjs"
},
"types": "./types/apidom-parser-adapter-json-schema-json-2020-12.d.ts",
"scripts": {
"build": "npm run clean && run-p --max-parallel ${CPU_CORES:-2} typescript:declaration build:es build:cjs build:umd:browser",
"build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'",
"build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'",
"build:umd:browser": "cross-env BABEL_ENV=browser webpack --config config/webpack/browser.config.js --progress",
"lint": "eslint ./",
"lint:fix": "eslint ./ --fix",
"clean": "rimraf --glob 'src/**/*.mjs' 'src/**/*.cjs' 'test/**/*.mjs' ./dist ./types",
"typescript:check-types": "tsc --noEmit && tsc -p ./test/tsconfig.json --noEmit",
"typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json",
"test": "npm run build:es && cross-env BABEL_ENV=es babel test --out-dir test --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward' && cross-env NODE_ENV=test mocha",
"prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .",
"postpack": "rimraf NOTICE LICENSES"
},
"repository": {
"type": "git",
"url": "git+https://github.com/swagger-api/apidom.git"
},
"author": "Vladimir Gorej",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime-corejs3": "^7.20.7",
"@swagger-api/apidom-core": "^1.0.0-beta.8",
"@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.8",
"@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.8",
"@types/ramda": "~0.30.0",
"ramda": "~0.30.0",
"ramda-adjunct": "^5.0.0"
},
"files": [
"src/**/*.mjs",
"src/**/*.cjs",
"dist/",
"types/apidom-parser-adapter-json-schema-json-2020-12.d.ts",
"LICENSES",
"NOTICE",
"README.md",
"CHANGELOG.md"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { propOr, omit } from 'ramda';
import { isNotUndefined } from 'ramda-adjunct';
import { ParseResultElement, createNamespace } from '@swagger-api/apidom-core';
import { parse as parseJSON, detect as detectJSON } from '@swagger-api/apidom-parser-adapter-json';
import jsonSchemaNamespace, { JSONSchemaElement } from '@swagger-api/apidom-ns-json-schema-2020-12';

export { default as mediaTypes } from './media-types.ts';

/**
* @public
*/
export const detectionRegExp =
/"\$schema"\s*:\s*"https:\/\/json-schema\.org\/draft\/(?<version_json>2020-12)\/schema"/;

/**
* @public
*/
export const detect = async (source: string): Promise<boolean> =>
detectionRegExp.test(source) && (await detectJSON(source));

/**
* @public
*/
export const parse = async (
source: string,
options: Record<string, unknown> = {},
): Promise<ParseResultElement> => {
const refractorOpts: Record<string, unknown> = propOr({}, 'refractorOpts', options);
const parserOpts = omit(['refractorOpts'], options);
const parseResultElement = await parseJSON(source, parserOpts);
const { result } = parseResultElement;

if (isNotUndefined(result)) {
const jsonSchemaElement = JSONSchemaElement.refract(result, refractorOpts);
jsonSchemaElement.classes.push('result');
parseResultElement.replaceResult(jsonSchemaElement);
}

return parseResultElement;
};

/**
* @public
*/
export const namespace = createNamespace(jsonSchemaNamespace);
Loading

0 comments on commit d7fcb96

Please sign in to comment.