From 6b208ed14940d3b71d79020dc93f931a417e337e Mon Sep 17 00:00:00 2001 From: Oliwia Rogala Date: Tue, 21 Jan 2025 10:54:19 +0100 Subject: [PATCH] feat: implement JSON Schema 2019-09 namespace (#4662) Refs #1819 --- package-lock.json | 19 + .../.eslintignore | 9 + .../apidom-ns-json-schema-2019-09/.gitignore | 7 + .../.mocharc.json | 5 + packages/apidom-ns-json-schema-2019-09/.npmrc | 2 + .../apidom-ns-json-schema-2019-09/README.md | 186 ++ .../config/api-extractor/api-extractor.json | 4 + .../config/webpack/browser.config.js | 70 + .../config/webpack/traits.config.js | 32 + .../package.json | 63 + .../src/elements/JSONSchema.ts | 275 +++ .../src/elements/LinkDescription.ts | 57 + .../src/index.ts | 107 + .../src/media-types.ts | 40 + .../src/namespace.ts | 20 + .../src/predicates.ts | 30 + .../src/refractor/index.ts | 57 + .../plugins/replace-empty-element.ts | 279 +++ .../src/refractor/registration.ts | 23 + .../src/refractor/specification.ts | 159 ++ .../src/refractor/toolbox.ts | 13 + .../visitors/json-schema/$defsVisitor.ts | 38 + .../visitors/json-schema/$refVisitor.ts | 23 + .../json-schema/$vocabularyVisitor.ts | 23 + .../visitors/json-schema/AllOfVisitor.ts | 45 + .../visitors/json-schema/AnyOfVisitor.ts | 45 + .../json-schema/DependentRequiredVisitor.ts | 23 + .../json-schema/DependentSchemasVisitor.ts | 38 + .../visitors/json-schema/ItemsVisitor.ts | 60 + .../visitors/json-schema/OneOfVisitor.ts | 45 + .../json-schema/PatternPropertiesVisitor.ts | 42 + .../visitors/json-schema/PropertiesVisitor.ts | 38 + .../refractor/visitors/json-schema/index.ts | 85 + .../json-schema/link-description/index.ts | 35 + .../src/traversal/visitor.ts | 20 + .../test/.eslintrc | 57 + .../test/mocha-bootstrap.ts | 11 + .../test/predicates.ts | 120 ++ .../refractor/__snapshots__/index.mjs.snap | 1762 +++++++++++++++++ .../JSONSchema/__snapshots__/index.mjs.snap | 1172 +++++++++++ .../refractor/elements/JSONSchema/index.ts | 171 ++ .../__snapshots__/index.mjs.snap | 66 + .../elements/LinkDescription/index.ts | 39 + .../fixtures/json-schema-2019-09.json | 62 + .../test/refractor/index.ts | 317 +++ .../__snapshots__/mappings.mjs.snap | 241 +++ .../__snapshots__/sequences.mjs.snap | 82 + .../plugins/replace-empty-element/mappings.ts | 350 ++++ .../replace-empty-element/sequences.ts | 141 ++ .../test/tsconfig.json | 10 + .../tsconfig.declaration.json | 9 + .../tsconfig.json | 6 + 52 files changed, 6633 insertions(+) create mode 100644 packages/apidom-ns-json-schema-2019-09/.eslintignore create mode 100644 packages/apidom-ns-json-schema-2019-09/.gitignore create mode 100644 packages/apidom-ns-json-schema-2019-09/.mocharc.json create mode 100644 packages/apidom-ns-json-schema-2019-09/.npmrc create mode 100644 packages/apidom-ns-json-schema-2019-09/README.md create mode 100644 packages/apidom-ns-json-schema-2019-09/config/api-extractor/api-extractor.json create mode 100644 packages/apidom-ns-json-schema-2019-09/config/webpack/browser.config.js create mode 100644 packages/apidom-ns-json-schema-2019-09/config/webpack/traits.config.js create mode 100644 packages/apidom-ns-json-schema-2019-09/package.json create mode 100644 packages/apidom-ns-json-schema-2019-09/src/elements/JSONSchema.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/elements/LinkDescription.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/media-types.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/namespace.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/predicates.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/plugins/replace-empty-element.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/registration.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/specification.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/toolbox.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$defsVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$refVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$vocabularyVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AllOfVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AnyOfVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentRequiredVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentSchemasVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/ItemsVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/OneOfVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PatternPropertiesVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PropertiesVisitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/link-description/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/.eslintrc create mode 100644 packages/apidom-ns-json-schema-2019-09/test/mocha-bootstrap.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/predicates.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/__snapshots__/index.mjs.snap create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/__snapshots__/index.mjs.snap create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/__snapshots__/index.mjs.snap create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/fixtures/json-schema-2019-09.json create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/index.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/mappings.mjs.snap create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/sequences.mjs.snap create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/mappings.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/sequences.ts create mode 100644 packages/apidom-ns-json-schema-2019-09/test/tsconfig.json create mode 100644 packages/apidom-ns-json-schema-2019-09/tsconfig.declaration.json create mode 100644 packages/apidom-ns-json-schema-2019-09/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 1ad22869d8..dd26b4273c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6742,6 +6742,10 @@ "resolved": "packages/apidom-ns-asyncapi-2", "link": true }, + "node_modules/@swagger-api/apidom-ns-json-schema-2019-09": { + "resolved": "packages/apidom-ns-json-schema-2019-09", + "link": true + }, "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { "resolved": "packages/apidom-ns-json-schema-draft-4", "link": true @@ -25478,6 +25482,21 @@ "ts-mixer": "^6.0.3" } }, + "packages/apidom-ns-json-schema-2019-09": { + "name": "@swagger-api/apidom-ns-json-schema-2019-09", + "version": "1.0.0-beta.6", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7", + "@swagger-api/apidom-core": "^1.0.0-beta.6", + "@swagger-api/apidom-error": "^1.0.0-beta.6", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.6", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, "packages/apidom-ns-json-schema-draft-4": { "name": "@swagger-api/apidom-ns-json-schema-draft-4", "version": "1.0.0-beta.6", diff --git a/packages/apidom-ns-json-schema-2019-09/.eslintignore b/packages/apidom-ns-json-schema-2019-09/.eslintignore new file mode 100644 index 0000000000..5637a4f9c6 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/.eslintignore @@ -0,0 +1,9 @@ +/**/*.js +/**/*.mjs +/**/*.cjs +/dist +/config +/types +/.eslintrc.js +/.nyc_output +/node_modules diff --git a/packages/apidom-ns-json-schema-2019-09/.gitignore b/packages/apidom-ns-json-schema-2019-09/.gitignore new file mode 100644 index 0000000000..9865229510 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/.gitignore @@ -0,0 +1,7 @@ +/src/**/*.mjs +/src/**/*.cjs +/test/**/*.mjs +/dist +/types +/NOTICE +/swagger-api-apidom-ns-json-schema-2019-09*.tgz diff --git a/packages/apidom-ns-json-schema-2019-09/.mocharc.json b/packages/apidom-ns-json-schema-2019-09/.mocharc.json new file mode 100644 index 0000000000..f4f6530232 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/.mocharc.json @@ -0,0 +1,5 @@ +{ + "recursive": true, + "spec": "test/**/*.mjs", + "file": ["test/mocha-bootstrap.mjs"] +} diff --git a/packages/apidom-ns-json-schema-2019-09/.npmrc b/packages/apidom-ns-json-schema-2019-09/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-ns-json-schema-2019-09/README.md b/packages/apidom-ns-json-schema-2019-09/README.md new file mode 100644 index 0000000000..5bf3d2567a --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/README.md @@ -0,0 +1,186 @@ +# @swagger-api/apidom-ns-json-schema-2019-09 + +`@swagger-api/apidom-ns-json-schema-2019-09` contains ApiDOM namespace specific to [JSON Schema 2019-09](https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02) specification. + +## Installation + +You can install this package via [npm CLI](https://docs.npmjs.com/cli) by running the following command: + +```sh + $ npm install @swagger-api/apidom-ns-json-schema-2019-09 +``` + +## JSON Schema 2019-09 namespace + +JSON Schema 2019-09 namespace consists of [number of elements](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-json-schema-2019-09/src/elements) implemented on top +of [primitive ones](https://github.com/refractproject/minim/tree/master/lib/primitives). + +```js +import { createNamespace } from '@swagger-api/apidom-core'; +import jsonShema201909Namespace from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const namespace = createNamespace(jsonShema201909Namespace); + +const objectElement = new namespace.elements.Object(); +const jsonSchemaElement = new namespace.elements.JSONSchema201909(); +``` + +When namespace instance is created in this way, it will extend the base namespace +with the namespace provided as an argument. + +Elements from the namespace can also be used directly by importing them. + +```js +import { JSONSchemaElement, LinkDescriptionElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const jsonSchemaElement = new JSONSchemaElement(); +const linkDescriptionElement = new LinkDescriptionElement(); +``` + +## Predicates + +This package exposes [predicates](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-json-schema-2019-09/src/predicates.ts) +for all higher order elements that are part of this namespace. + +```js +import { isJSONSchemaElement, JSONSchemaElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const jsonSchemaElement = new JSONSchemaElement(); + +isJSONSchemaElement(jsonSchemaElement); // => true +``` + +## Traversal + +Traversing ApiDOM in this namespace is possible by using `visit` function from `apidom` package. +This package comes with its own [keyMap](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts#L11) and [nodeTypeGetter](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts#L4). +To learn more about these `visit` configuration options please refer to [@swagger-api/apidom-ast documentation](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ast/README.md#visit). + +```js +import { visit } from '@swagger-api/apidom-core'; +import { JSONSchemaElement, keyMap, getNodeType } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const element = new JSONSchemaElement(); + +const visitor = { + JSONSchema201909Element(jsonSchemaElement) { + console.dir(jsonSchemaElement); + }, +}; + +visit(element, visitor, { keyMap, nodeTypeGetter: getNodeType }); +``` + +## Refractors + +Refractor is a special layer inside the namespace that can transform either JavaScript structures +or generic ApiDOM structures into structures built from elements of this namespace. + +**Refracting JavaScript structures**: + +```js +import { LinkDescriptionElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const object = { + anchor: 'nodes/{thisNodeId}', + anchorPointer: '#/relative/json/pointer', +}; + +LinkDescriptionElement.refract(object); // => LinkDescriptionElement({ anchor, anchorPointer }) +``` + +**Refracting generic ApiDOM structures**: + +```js +import { ObjectElement } from '@swagger-api/apidom-core'; +import { LinkDescriptionElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const objectElement = new ObjectElement({ + anchor: 'nodes/{thisNodeId}', + anchorPointer: '#/relative/json/pointer', +}); + +LinkDescriptionElement.refract(objectElement); // => LinkDescriptionElement({ anchor = 'nodes/{thisNodeId}', anchorPointer = '#/relative/json/pointer' }) +``` + +### Refractor plugins + +Refractors can accept plugins as a second argument of refract static method. + +```js +import { ObjectElement } from '@swagger-api/apidom-core'; +import { LinkDescriptionElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const objectElement = new ObjectElement({ + anchor: 'nodes/{thisNodeId}', + anchorPointer: '#/relative/json/pointer', +}); + +const plugin = ({ predicates, namespace }) => ({ + name: 'plugin', + pre() { + console.dir('runs before traversal'); + }, + visitor: { + LinkDescriptionElement(linkDescriptionElement) { + linkDescriptionElement.anchorPointer = '#/relative/json/pointer/x'; + }, + }, + post() { + console.dir('runs after traversal'); + }, +}); + +LinkDescriptionElement.refract(objectElement, { plugins: [plugin] }); // => LinkDescriptionElement({ anchor = 'nodes/{thisNodeId}', anchorPointer = '#/relative/json/pointer/x' }) +``` + +You can define as many plugins as needed to enhance the resulting namespaced ApiDOM structure. +If multiple plugins with the same visitor method are defined, they run in parallel (just like in Babel). + +#### Replace Empty Element plugin + +This plugin is specific to YAML 1.2 format, which allows defining key-value pairs with empty key, +empty value, or both. If the value is not provided in YAML format, this plugin compensates for +this missing value with the most appropriate semantic element type. + +```js +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; +import { refractorPluginReplaceEmptyElement, JSONSchemaElement } from '@swagger-api/apidom-ns-json-schema-2019-09'; + +const yamlDefinition = ` +$schema: 'https://json-schema.org/draft/2019-09/schema#' +if: +`; +const apiDOM = await parse(yamlDefinition); +const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], +}); + +// => +// (JSONSchema201909Element +// (MemberElement +// (StringElement) +// (StringElement)) +// (MemberElement +// (StringElement) +// (JSONSchema201909Element))) + +// => without the plugin the result would be as follows: +// (JSONSchema201909Element +// (MemberElement +// (StringElement) +// (StringElement)) +// (MemberElement +// (StringElement) +// (StringElement))) +``` + +## Implementation progress + +Only fully implemented specification objects should be checked here. + +- [x] [JSON Schema Object](https://json-schema.org/draft/2019-09/json-schema-core) +- [x] [Link Description Object](https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#rfc.section.6) + + + diff --git a/packages/apidom-ns-json-schema-2019-09/config/api-extractor/api-extractor.json b/packages/apidom-ns-json-schema-2019-09/config/api-extractor/api-extractor.json new file mode 100644 index 0000000000..7de0d99447 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/config/api-extractor/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../../../api-extractor.json" +} diff --git a/packages/apidom-ns-json-schema-2019-09/config/webpack/browser.config.js b/packages/apidom-ns-json-schema-2019-09/config/webpack/browser.config.js new file mode 100644 index 0000000000..f7b103cc98 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/config/webpack/browser.config.js @@ -0,0 +1,70 @@ +import path from 'node:path'; +import { nonMinimizeTrait, minimizeTrait } from './traits.config.js'; + +const browser = { + mode: 'production', + entry: ['./src/index.ts'], + target: 'web', + performance: { + maxEntrypointSize: 1400000, + maxAssetSize: 1400000, + }, + output: { + path: path.resolve('./dist'), + filename: 'apidom-ns-json-schema-2019-09.browser.js', + libraryTarget: 'umd', + library: 'apidomNsJSONSchema201909', + }, + resolve: { + extensions: ['.ts', '.mjs', '.js', '.json'], + }, + module: { + rules: [ + { + test: /\.(ts|js)?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + babelrc: true, + rootMode: 'upward', + }, + }, + }, + ], + }, + ...nonMinimizeTrait, +}; + +const browserMin = { + mode: 'production', + entry: ['./src/index.ts'], + target: 'web', + output: { + path: path.resolve('./dist'), + filename: 'apidom-ns-json-schema-2019-09.browser.js', + libraryTarget: 'umd', + library: 'apidomNsJSONSchema201909', + }, + resolve: { + extensions: ['.ts', '.mjs', '.js', '.json'], + }, + module: { + rules: [ + { + test: /\.(ts|js)?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + babelrc: true, + rootMode: 'upward', + }, + }, + }, + ], + }, + ...minimizeTrait, +}; + +export default [browser, browserMin]; diff --git a/packages/apidom-ns-json-schema-2019-09/config/webpack/traits.config.js b/packages/apidom-ns-json-schema-2019-09/config/webpack/traits.config.js new file mode 100644 index 0000000000..9043521175 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/config/webpack/traits.config.js @@ -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, + }, + }, + }), + ], + }, +}; diff --git a/packages/apidom-ns-json-schema-2019-09/package.json b/packages/apidom-ns-json-schema-2019-09/package.json new file mode 100644 index 0000000000..fbbb72aed5 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/package.json @@ -0,0 +1,63 @@ +{ + "name": "@swagger-api/apidom-ns-json-schema-2019-09", + "version": "1.0.0-beta.6", + "description": "JSON Schema 2019-09 namespace for ApiDOM.", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "type": "module", + "sideEffects": [ + "./src/refractor/registration.mjs", + "./src/refractor/registration.cjs" + ], + "unpkg": "./dist/apidom-ns-json-schema-2019-09.browser.min.js", + "main": "./src/index.cjs", + "exports": { + "types": "./types/apidom-ns-json-schema-2019-09.d.ts", + "import": "./src/index.mjs", + "require": "./src/index.cjs" + }, + "types": "./types/apidom-ns-json-schema-2019-09.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", + "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", + "test:update-snapshots": "cross-env UPDATE_SNAPSHOT=1 mocha", + "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 2>&1 | shx grep -v 'Visitor_base'", + "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.6", + "@swagger-api/apidom-error": "^1.0.0-beta.6", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.6", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + }, + "files": [ + "src/**/*.mjs", + "src/**/*.cjs", + "dist/", + "types/apidom-ns-json-schema-2019-09.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-ns-json-schema-2019-09/src/elements/JSONSchema.ts b/packages/apidom-ns-json-schema-2019-09/src/elements/JSONSchema.ts new file mode 100644 index 0000000000..434c57973d --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/elements/JSONSchema.ts @@ -0,0 +1,275 @@ +import { + StringElement, + ObjectElement, + NumberElement, + ArrayElement, + BooleanElement, + Attributes, + Meta, +} from '@swagger-api/apidom-core'; +import { UnsupportedOperationError } from '@swagger-api/apidom-error'; +import { JSONSchemaElement } from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/* eslint-disable class-methods-use-this */ + +/** + * @public + */ +class JSONSchema extends JSONSchemaElement { + constructor(content?: Record, meta?: Meta, attributes?: Attributes) { + super(content, meta, attributes); + this.element = 'JSONSchema201909'; + } + + /** + * Core vocabulary + * + * URI: https://json-schema.org/draft/2019-09/vocab/core + */ + + get $vocabulary(): ObjectElement | undefined { + return this.get('$vocabulary'); + } + + set $vocabulary($vocabulary: ObjectElement | undefined) { + this.set('$vocabulary', $vocabulary); + } + + get $anchor(): StringElement | undefined { + return this.get('$anchor'); + } + + set $anchor($anchor: StringElement | undefined) { + this.set('$anchor', $anchor); + } + + get $recursiveAnchor(): BooleanElement | undefined { + return this.get('$recursiveAnchor'); + } + + set $recursiveAnchor($recursiveAnchor: BooleanElement | undefined) { + this.set('$recursiveAnchor', $recursiveAnchor); + } + + get $recursiveRef(): StringElement | undefined { + return this.get('$recursiveRef'); + } + + set $recursiveRef($recursiveRef: StringElement | undefined) { + this.set('$recursiveRef', $recursiveRef); + } + + get $ref(): StringElement | undefined { + return this.get('$ref'); + } + + set $ref($ref: StringElement | undefined) { + this.set('$ref', $ref); + } + + get $defs(): ObjectElement | undefined { + return this.get('$defs'); + } + + set $defs($defs: ObjectElement | undefined) { + this.set('$defs', $defs); + } + + get definitions(): ObjectElement | undefined { + throw new UnsupportedOperationError( + 'definitions keyword from Validation vocabulary has been renamed to $defs.', + ); + } + + set definitions(definitions: ObjectElement | undefined) { + throw new UnsupportedOperationError( + 'definitions keyword from Validation vocabulary has been renamed to $defs.', + ); + } + + /** + * Applicator vocabulary + * + * URI: https://json-schema.org/draft/2019-09/vocab/applicator + */ + + get not(): this | BooleanElement | undefined { + return this.get('not'); + } + + set not(not: this | BooleanElement | undefined) { + this.set('not', not); + } + + get if(): this | BooleanElement | undefined { + return this.get('if'); + } + + set if(ifSchema: this | BooleanElement | undefined) { + this.set('if', ifSchema); + } + + get then(): this | BooleanElement | undefined { + return this.get('then'); + } + + set then(thenSchema: this | BooleanElement | undefined) { + this.set('then', thenSchema); + } + + get else(): this | BooleanElement | undefined { + return this.get('else'); + } + + set else(elseSchema: this | BooleanElement | undefined) { + this.set('else', elseSchema); + } + + get dependentSchemas(): ObjectElement | undefined { + return this.get('dependentSchemas'); + } + + set dependentSchemas(dependentSchemas: ObjectElement | undefined) { + this.set('dependentSchemas', dependentSchemas); + } + + get dependencies(): ObjectElement | undefined { + throw new UnsupportedOperationError( + 'dependencies keyword from Validation vocabulary has been renamed to dependentSchemas.', + ); + } + + set dependencies(dependencies: ObjectElement | undefined) { + throw new UnsupportedOperationError( + 'dependencies keyword from Validation vocabulary has been renamed to dependentSchemas.', + ); + } + + get items(): this | BooleanElement | ArrayElement | undefined { + return this.get('items'); + } + + set items(items: this | BooleanElement | ArrayElement | undefined) { + this.set('items', items); + } + + get containsProp(): this | BooleanElement | undefined { + return this.get('contains'); + } + + set containsProp(containsProp: this | BooleanElement | undefined) { + this.set('contains', containsProp); + } + + get additionalProperties(): this | BooleanElement | undefined { + return this.get('additionalProperties'); + } + + set additionalProperties(additionalProperties: this | BooleanElement | undefined) { + this.set('additionalProperties', additionalProperties); + } + + get additionalItems(): this | BooleanElement | undefined { + return this.get('additionalItems'); + } + + set additionalItems(additionalItems: this | BooleanElement | undefined) { + this.set('additionalItems', additionalItems); + } + + get propertyNames(): this | BooleanElement | undefined { + return this.get('propertyNames'); + } + + set propertyNames(propertyNames: this | BooleanElement | undefined) { + this.set('propertyNames', propertyNames); + } + + get unevaluatedItems(): this | BooleanElement | undefined { + return this.get('unevaluatedItems'); + } + + set unevaluatedItems(unevaluatedItems: this | BooleanElement | undefined) { + this.set('unevaluatedItems', unevaluatedItems); + } + + get unevaluatedProperties(): this | BooleanElement | undefined { + return this.get('unevaluatedProperties'); + } + + set unevaluatedProperties(unevaluatedProperties: this | BooleanElement | undefined) { + this.set('unevaluatedProperties', unevaluatedProperties); + } + + /** + * Validation vocabulary + * + * URI: https://json-schema.org/draft/2019-09/json-schema-validation#rfc.section.6 + */ + + /** + * Validation Keywords for Arrays + * + * URI: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-validation-02#rfc.section.6.4 + */ + + get maxContains(): NumberElement | undefined { + return this.get('maxContains'); + } + + set maxContains(maxContains: NumberElement | undefined) { + this.set('maxContains', maxContains); + } + + get minContains(): NumberElement | undefined { + return this.get('minContains'); + } + + set minContains(minContains: NumberElement | undefined) { + this.set('minContains', minContains); + } + + /** + * Validation Keywords for Objects + * + * URI: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-validation-02#rfc.section.6.5 + */ + + get dependentRequired(): ObjectElement | undefined { + return this.get('dependentRequired'); + } + + set dependentRequired(dependentRequired: ObjectElement | undefined) { + this.set('dependentRequired', dependentRequired); + } + + /** + * Vocabulary for Basic Meta-Data Annotations + * + * URI: https://json-schema.org/draft/2019-09/vocab/meta-data + */ + + get deprecated(): BooleanElement | undefined { + return this.get('deprecated'); + } + + set deprecated(deprecated: BooleanElement | undefined) { + this.set('deprecated', deprecated); + } + + /** + * Vocabulary for the Contents of String-Encoded Data + * + * URI: https://json-schema.org/draft/2019-09/vocab/content + */ + + get contentSchema(): this | BooleanElement | undefined { + return this.get('contentSchema'); + } + + set contentSchema(contentSchema: this | BooleanElement | undefined) { + this.set('contentSchema', contentSchema); + } +} + +export default JSONSchema; diff --git a/packages/apidom-ns-json-schema-2019-09/src/elements/LinkDescription.ts b/packages/apidom-ns-json-schema-2019-09/src/elements/LinkDescription.ts new file mode 100644 index 0000000000..4ac01a9a32 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/elements/LinkDescription.ts @@ -0,0 +1,57 @@ +import { BooleanElement } from '@swagger-api/apidom-core'; +import { LinkDescriptionElement } from '@swagger-api/apidom-ns-json-schema-draft-7'; + +import JSONSchema from './JSONSchema.ts'; + +/* eslint-disable class-methods-use-this */ + +/** + * URI: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#rfc.section.6 + * @public + */ + +class LinkDescription extends LinkDescriptionElement { + /** + * Link Target Attributes. + * + * URI: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#rfc.section.6.5 + */ + get targetSchema(): JSONSchema | BooleanElement | undefined { + return this.get('targetSchema'); + } + + set targetSchema(targetSchema: JSONSchema | BooleanElement | undefined) { + this.set('targetSchema', targetSchema); + } + + /** + * Link Input. + * + * URI: https://json-schema.org/draft/2019-09/draft-handrews-json-schema-hyperschema-02#input + */ + get hrefSchema(): JSONSchema | BooleanElement | undefined { + return this.get('hrefSchema'); + } + + set hrefSchema(hrefSchema: JSONSchema | BooleanElement | undefined) { + this.set('hrefSchema', hrefSchema); + } + + get headerSchema(): JSONSchema | BooleanElement | undefined { + return this.get('headerSchema'); + } + + set headerSchema(headerSchema: JSONSchema | BooleanElement | undefined) { + this.set('headerSchema', headerSchema); + } + + get submissionSchema(): JSONSchema | BooleanElement | undefined { + return this.get('submissionSchema'); + } + + set submissionSchema(submissionSchema: JSONSchema | BooleanElement | undefined) { + this.set('submissionSchema', submissionSchema); + } +} + +export default LinkDescription; diff --git a/packages/apidom-ns-json-schema-2019-09/src/index.ts b/packages/apidom-ns-json-schema-2019-09/src/index.ts new file mode 100644 index 0000000000..370bf61163 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/index.ts @@ -0,0 +1,107 @@ +export { + isRefElement, + isLinkElement, + isMemberElement, + isObjectElement, + isArrayElement, + isBooleanElement, + isNullElement, + isElement, + isNumberElement, + isStringElement, +} from '@swagger-api/apidom-core'; + +export { default as mediaTypes, JSONSchema201909MediaTypes } from './media-types.ts'; +export type { Format } from './media-types.ts'; + +// eslint-disable-next-line no-restricted-exports +export { default } from './namespace.ts'; + +export { default as refractorPluginReplaceEmptyElement } from './refractor/plugins/replace-empty-element.ts'; + +export { default as refract, createRefractor } from './refractor/index.ts'; +export { default as specificationObj } from './refractor/specification.ts'; + +export { isJSONSchemaElement, isLinkDescriptionElement } from './predicates.ts'; + +export { + SpecificationVisitor, + FallbackVisitor, + FixedFieldsVisitor, + PatternedFieldsVisitor, + MapVisitor, + AlternatingVisitor, + ParentSchemaAwareVisitor, + Visitor, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; +export type { + SpecificationVisitorOptions, + FallbackVisitorOptions, + FixedFieldsVisitorOptions, + PatternedFieldsVisitorOptions, + MapVisitorOptions, + AlternatingVisitorOptions, + ParentSchemaAwareVisitorOptions, + VisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +export type { + default as JSONSchemaVisitor, + JSONSchemaVisitorOptions, +} from './refractor/visitors/json-schema/index.ts'; +export type { + default as LinkDescriptionVisitor, + LinkDescriptionVisitorOptions, +} from './refractor/visitors/json-schema/link-description/index.ts'; +export type { + default as $defsVisitor, + $defsVisitorOptions, +} from './refractor/visitors/json-schema/$defsVisitor.ts'; +export type { + default as $refVisitor, + $refVisitorOptions, +} from './refractor/visitors/json-schema/$refVisitor.ts'; +export type { + default as $vocabularyVisitor, + $vocabularyVisitorOptions, +} from './refractor/visitors/json-schema/$vocabularyVisitor.ts'; +export type { + default as AllOfVisitor, + AllOfVisitorOptions, +} from './refractor/visitors/json-schema/AllOfVisitor.ts'; +export type { + default as AnyOfVisitor, + AnyOfVisitorOptions, +} from './refractor/visitors/json-schema/AnyOfVisitor.ts'; +export type { + default as DependentRequiredVisitor, + DependentRequiredVisitorOptions, +} from './refractor/visitors/json-schema/DependentRequiredVisitor.ts'; +export type { + default as DependentSchemasVisitor, + DependentSchemasVisitorOptions, +} from './refractor/visitors/json-schema/DependentSchemasVisitor.ts'; +export type { + default as ItemsVisitor, + ItemsVisitorOptions, +} from './refractor/visitors/json-schema/ItemsVisitor.ts'; +export type { + default as OneOfVisitor, + OneOfVisitorOptions, +} from './refractor/visitors/json-schema/OneOfVisitor.ts'; +export type { + default as PatternPropertiesVisitor, + PatternPropertiesVisitorOptions, +} from './refractor/visitors/json-schema/PatternPropertiesVisitor.ts'; +export type { + default as PropertiesVisitor, + PropertiesVisitorOptions, +} from './refractor/visitors/json-schema/PropertiesVisitor.ts'; + +export { keyMap, getNodeType } from './traversal/visitor.ts'; + +/** + * JSON Schema 2019-09 specification elements. + */ +export { JSONSchemaElement, LinkDescriptionElement } from './refractor/registration.ts'; diff --git a/packages/apidom-ns-json-schema-2019-09/src/media-types.ts b/packages/apidom-ns-json-schema-2019-09/src/media-types.ts new file mode 100644 index 0000000000..0e2f3c6161 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/media-types.ts @@ -0,0 +1,40 @@ +import { last } from 'ramda'; +import { MediaTypes } from '@swagger-api/apidom-core'; + +/** + * @public + */ +export type Format = 'generic' | 'json' | 'yaml'; + +/** + * @public + */ +export class JSONSchema201909MediaTypes extends MediaTypes { + filterByFormat(format: Format = 'generic') { + const effectiveFormat = format === 'generic' ? 'schema;version' : format; + return this.filter((mediaType) => mediaType.includes(effectiveFormat)); + } + + findBy(version = '2019-09', format: Format = 'generic') { + const search = + format === 'generic' ? `schema;version=${version}` : `schema+${format};version=${version}`; + const found = this.find((mediaType) => mediaType.includes(search)); + + return found || this.unknownMediaType; + } + + latest(format: Format = 'generic') { + return last(this.filterByFormat(format)) as string; + } +} + +/** + * @public + */ +const mediaTypes = new JSONSchema201909MediaTypes( + 'application/schema;version=2019-09', + 'application/schema+json;version=2019-09', + 'application/schema+yaml;version=2019-09', +); + +export default mediaTypes; diff --git a/packages/apidom-ns-json-schema-2019-09/src/namespace.ts b/packages/apidom-ns-json-schema-2019-09/src/namespace.ts new file mode 100644 index 0000000000..c0899899a1 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/namespace.ts @@ -0,0 +1,20 @@ +import { NamespacePluginOptions } from '@swagger-api/apidom-core'; + +import JSONSchemaElement from './elements/JSONSchema.ts'; +import LinkDescriptionElement from './elements/LinkDescription.ts'; + +/** + * @public + */ +const jsonSchema201909 = { + namespace: (options: NamespacePluginOptions) => { + const { base } = options; + + base.register('jSONSchema201909', JSONSchemaElement); + base.register('linkDescription', LinkDescriptionElement); + + return base; + }, +}; + +export default jsonSchema201909; diff --git a/packages/apidom-ns-json-schema-2019-09/src/predicates.ts b/packages/apidom-ns-json-schema-2019-09/src/predicates.ts new file mode 100644 index 0000000000..4b5c03b479 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/predicates.ts @@ -0,0 +1,30 @@ +import { createPredicate } from '@swagger-api/apidom-core'; + +import JSONSchemaElement from './elements/JSONSchema.ts'; +import LinkDescriptionElement from './elements/LinkDescription.ts'; + +/** + * @public + */ +export const isJSONSchemaElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq }) => { + return (element: unknown): element is JSONSchemaElement => + element instanceof JSONSchemaElement || + (hasBasicElementProps(element) && + isElementType('JSONSchema201909', element) && + primitiveEq('object', element)); + }, +); + +/** + * @public + */ +export const isLinkDescriptionElement = createPredicate( + ({ hasBasicElementProps, isElementType, primitiveEq }) => { + return (element: unknown): element is LinkDescriptionElement => + element instanceof LinkDescriptionElement || + (hasBasicElementProps(element) && + isElementType('linkDescription', element) && + primitiveEq('object', element)); + }, +); diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/index.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/index.ts new file mode 100644 index 0000000000..7d3f606a01 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/index.ts @@ -0,0 +1,57 @@ +import { path } from 'ramda'; +import { + visit, + Element, + dereference, + refract as baseRefract, + dispatchRefractorPlugins, +} from '@swagger-api/apidom-core'; +import type { Visitor as VisitorClass } from '@swagger-api/apidom-ns-json-schema-draft-7'; + +import specification from './specification.ts'; +import { keyMap, getNodeType } from '../traversal/visitor.ts'; +import createToolbox from './toolbox.ts'; + +/** + * @public + */ +const refract = ( + value: unknown, + { + specPath = ['visitors', 'document', 'objects', 'JSONSchema', '$visitor'], + plugins = [], + specificationObj = specification, + } = {}, +): T => { + const element = baseRefract(value); + const resolvedSpec = dereference(specificationObj); + + /** + * This is where generic ApiDOM becomes semantic (namespace applied). + * We don't allow consumers to hook into this translation. + * Though we allow consumers to define their onw plugins on already transformed ApiDOM. + */ + const RootVisitorClass = path(specPath, resolvedSpec) as typeof VisitorClass; + const rootVisitor = new RootVisitorClass({ specObj: resolvedSpec }); + + visit(element, rootVisitor); + + /** + * Run plugins only when necessary. + * Running plugins visitors means extra single traversal === performance hit. + */ + return dispatchRefractorPlugins(rootVisitor.element, plugins, { + toolboxCreator: createToolbox, + visitorOptions: { keyMap, nodeTypeGetter: getNodeType }, + }); +}; + +/** + * @public + */ +export const createRefractor = + (specPath: string[]) => + (value: unknown, options = {}) => + refract(value, { specPath, ...options }); + +export default refract; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/plugins/replace-empty-element.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/plugins/replace-empty-element.ts new file mode 100644 index 0000000000..5875ecb823 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/plugins/replace-empty-element.ts @@ -0,0 +1,279 @@ +import { + ArrayElement, + ObjectElement, + StringElement, + isStringElement, + isArrayElement, + isElement, + isMemberElement, + includesClasses, + cloneDeep, + toValue, +} from '@swagger-api/apidom-core'; +/** + * JSON Schema 2019-09 specification elements. + */ + +import JSONSchemaElement from '../../elements/JSONSchema.ts'; +import LinkDescriptionElement from '../../elements/LinkDescription.ts'; +import { getNodeType } from '../../traversal/visitor.ts'; + +/** + * This plugin is specific to YAML 1.2 format, which allows defining key-value pairs + * with empty key, empty value, or both. If the value is not provided in YAML format, + * this plugin compensates for this missing value with the most appropriate semantic element type. + * + * https://yaml.org/spec/1.2.2/#72-empty-nodes + * + * @example + * + * ```yaml + * $schema: https://json-schema.org/draft/2019-09/schema + * items: + * ``` + * Refracting result without this plugin: + * + * (JSONSchemaElement + * (MemberElement + * (StringElement) + * (StringElement)) + * (MemberElement + * (StringElement) + * (StringElement)) + * + * Refracting result with this plugin: + * + * (JSONSchemaElement + * (MemberElement + * (StringElement) + * (StringElement)) + * (MemberElement + * (StringElement) + * (JSONSchemaElement)) + */ + +const isEmptyElement = (element: any) => + isStringElement(element) && includesClasses(['yaml-e-node', 'yaml-e-scalar'], element); + +const schema = { + JSONSchema201909Element: { + additionalItems(...args: any[]) { + return new JSONSchemaElement(...args); + }, + items(...args: any[]) { + return new JSONSchemaElement(...args); + }, + contains(...args: any[]) { + return new JSONSchemaElement(...args); + }, + required(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-required'); + return element; + }, + properties(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-properties'); + return element; + }, + additionalProperties(...args: any[]) { + return new JSONSchemaElement(...args); + }, + patternProperties(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-patternProperties'); + return element; + }, + dependentSchemas(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-dependentSchemas'); + return element; + }, + propertyNames(...args: any[]) { + return new JSONSchemaElement(...args); + }, + enum(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-enum'); + return element; + }, + allOf(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-allOf'); + return element; + }, + anyOf(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-anyOf'); + return element; + }, + oneOf(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-oneOf'); + return element; + }, + if(...args: any[]) { + return new JSONSchemaElement(...args); + }, + then(...args: any[]) { + return new JSONSchemaElement(...args); + }, + else(...args: any[]) { + return new JSONSchemaElement(...args); + }, + not(...args: any[]) { + return new JSONSchemaElement(...args); + }, + $defs(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-$defs'); + return element; + }, + examples(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-examples'); + return element; + }, + links(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-links'); + return element; + }, + $vocabulary(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-$vocabulary'); + return element; + }, + unevaluatedItems(...args: any[]) { + return new JSONSchemaElement(...args); + }, + unevaluatedProperties(...args: any[]) { + return new JSONSchemaElement(...args); + }, + $dependentRequired(...args: any[]) { + const element = new ObjectElement(...args); + element.classes.push('json-schema-$dependentRequired'); + return element; + }, + contentSchema(...args: any[]) { + return new JSONSchemaElement(...args); + }, + type(...args: any[]) { + const element = new ArrayElement(...args); + element.classes.push('json-schema-type'); + return element; + }, + }, + LinkDescriptionElement: { + hrefSchema(...args: any[]) { + return new JSONSchemaElement(...args); + }, + targetSchema(...args: any[]) { + return new JSONSchemaElement(...args); + }, + submissionSchema(...args: any[]) { + return new JSONSchemaElement(...args); + }, + templatePointers(...args: any[]) { + return new ObjectElement(...args); + }, + templateRequired(...args: any[]) { + return new ArrayElement(...args); + }, + targetHints(...args: any[]) { + return new ObjectElement(...args); + }, + headerSchema(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-properties': { + '[key: *]': function key(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-patternProperties': { + '[key: *]': function key(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-dependentSchemas': { + '[key: *]': function key(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-allOf': { + '<*>': function asterisk(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-anyOf': { + '<*>': function asterisk(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-oneOf': { + '<*>': function asterisk(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-$defs': { + '[key: *]': function key(...args: any[]) { + return new JSONSchemaElement(...args); + }, + }, + 'json-schema-links': { + '<*>': function asterisk(...args: any[]) { + return new LinkDescriptionElement(...args); + }, + }, +}; + +const findElementFactory = (ancestor: any, keyName: string) => { + const elementType = getNodeType(ancestor); // @ts-ignore + const keyMapping = schema[elementType] || schema[toValue(ancestor.classes.first)]; + + return typeof keyMapping === 'undefined' + ? undefined + : Object.prototype.hasOwnProperty.call(keyMapping, '[key: *]') + ? keyMapping['[key: *]'] + : keyMapping[keyName]; +}; + +/** + * @public + */ +const plugin = () => () => { + return { + visitor: { + StringElement(element: StringElement, key: any, parent: any, path: any, ancestors: any[]) { + if (!isEmptyElement(element)) return undefined; + + const lineage = [...ancestors, parent].filter(isElement); + const parentElement = lineage[lineage.length - 1]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future + let elementFactory; + let context; + + if (isArrayElement(parentElement)) { + context = element; + elementFactory = findElementFactory(parentElement, '<*>'); + } else if (isMemberElement(parentElement)) { + context = lineage[lineage.length - 2]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future + elementFactory = findElementFactory(context, toValue(parentElement.key)); + } + + // no element factory found + if (typeof elementFactory !== 'function') return undefined; + + return elementFactory.call( + { context }, + undefined, + cloneDeep(element.meta), + cloneDeep(element.attributes), + ); + }, + }, + }; +}; + +export default plugin; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/registration.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/registration.ts new file mode 100644 index 0000000000..e419d6a6c4 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/registration.ts @@ -0,0 +1,23 @@ +import JSONSchemaElement from '../elements/JSONSchema.ts'; +import LinkDescriptionElement from '../elements/LinkDescription.ts'; +import { createRefractor } from './index.ts'; + +// register refractors specific to element types + +JSONSchemaElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'JSONSchema', + '$visitor', +]); + +LinkDescriptionElement.refract = createRefractor([ + 'visitors', + 'document', + 'objects', + 'LinkDescription', + '$visitor', +]); + +export { JSONSchemaElement, LinkDescriptionElement }; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/specification.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/specification.ts new file mode 100644 index 0000000000..ac7cbb5291 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/specification.ts @@ -0,0 +1,159 @@ +import { pipe, assocPath, dissocPath } from 'ramda'; +import { specificationObj } from '@swagger-api/apidom-ns-json-schema-draft-7'; + +import JSONSchemaVisitor from './visitors/json-schema/index.ts'; +import JSONSchema$vocabularyVisitor from './visitors/json-schema/$vocabularyVisitor.ts'; +import JSONSchema$refVisitor from './visitors/json-schema/$refVisitor.ts'; +import JSONSchema$defsVisitor from './visitors/json-schema/$defsVisitor.ts'; +import JSONSchemaAllOfVisitor from './visitors/json-schema/AllOfVisitor.ts'; +import JSONSchemaAnyOfVisitor from './visitors/json-schema/AnyOfVisitor.ts'; +import JSONSchemaOneOfVisitor from './visitors/json-schema/OneOfVisitor.ts'; +import JSONSchemaDependentSchemasVisitor from './visitors/json-schema/DependentSchemasVisitor.ts'; +import JSONSchemaItemsVisitor from './visitors/json-schema/ItemsVisitor.ts'; +import JSONSchemaPropertiesVisitor from './visitors/json-schema/PropertiesVisitor.ts'; +import JSONSchemaPatternPropertiesVisitor from './visitors/json-schema/PatternPropertiesVisitor.ts'; +import JSONSchemaDependentRequiredVisitor from './visitors/json-schema/DependentRequiredVisitor.ts'; +import JSONSchemaLinkDescriptionVisitor from './visitors/json-schema/link-description/index.ts'; + +const specification = pipe( + // JSON Schema object modifications + assocPath(['visitors', 'document', 'objects', 'JSONSchema', '$visitor'], JSONSchemaVisitor), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$vocabulary'], + JSONSchema$vocabularyVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$anchor'], + specificationObj.visitors.value, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$recursiveAnchor'], + specificationObj.visitors.value, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$recursiveRef'], + specificationObj.visitors.value, + ), + dissocPath(['visitors', 'document', 'objects', 'JSONReference', '$visitor']), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$ref'], + JSONSchema$refVisitor, + ), + dissocPath(['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'definitions']), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', '$defs'], + JSONSchema$defsVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'allOf'], + JSONSchemaAllOfVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'anyOf'], + JSONSchemaAnyOfVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'oneOf'], + JSONSchemaOneOfVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'not'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'if'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'then'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'else'], + JSONSchemaVisitor, + ), + dissocPath(['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'dependencies']), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'dependentSchemas'], + JSONSchemaDependentSchemasVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'items'], + JSONSchemaItemsVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'contains'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'properties'], + JSONSchemaPropertiesVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'patternProperties'], + JSONSchemaPatternPropertiesVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'additionalProperties'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'additionalItems'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'propertyNames'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'unevaluatedItems'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'unevaluatedProperties'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'maxContains'], + specificationObj.visitors.value, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'minContains'], + specificationObj.visitors.value, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'dependentRequired'], + JSONSchemaDependentRequiredVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'deprecated'], + specificationObj.visitors.value, + ), + assocPath( + ['visitors', 'document', 'objects', 'JSONSchema', 'fixedFields', 'contentSchema'], + JSONSchemaVisitor, + ), + // Link Description object modifications + assocPath( + ['visitors', 'document', 'objects', 'LinkDescription', '$visitor'], + JSONSchemaLinkDescriptionVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'LinkDescription', 'fixedFields', 'targetSchema'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'LinkDescription', 'fixedFields', 'hrefSchema'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'LinkDescription', 'fixedFields', 'headerSchema'], + JSONSchemaVisitor, + ), + assocPath( + ['visitors', 'document', 'objects', 'LinkDescription', 'fixedFields', 'submissionSchema'], + JSONSchemaVisitor, + ), +)(specificationObj); + +export default specification as typeof specificationObj; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/toolbox.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/toolbox.ts new file mode 100644 index 0000000000..9fa67bb057 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/toolbox.ts @@ -0,0 +1,13 @@ +import { createNamespace, isStringElement } from '@swagger-api/apidom-core'; + +import * as jsonSchema201909Predicates from '../predicates.ts'; +import jsonSchema201909Namespace from '../namespace.ts'; + +const createToolbox = () => { + const namespace = createNamespace(jsonSchema201909Namespace); + const predicates = { ...jsonSchema201909Predicates, isStringElement }; + + return { predicates, namespace }; +}; + +export default createToolbox; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$defsVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$defsVisitor.ts new file mode 100644 index 0000000000..e8ae363580 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$defsVisitor.ts @@ -0,0 +1,38 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + MapVisitor, + MapVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface $defsVisitorOptions + extends MapVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class $defsVisitor extends Mixin(MapVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ObjectElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'JSONSchema']>; + + constructor(options: $defsVisitorOptions) { + super(options); + this.element = new ObjectElement(); + this.element.classes.push('json-schema-$defs'); + this.specPath = always(['document', 'objects', 'JSONSchema']); + } +} + +export default $defsVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$refVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$refVisitor.ts new file mode 100644 index 0000000000..6a5afd9d8a --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$refVisitor.ts @@ -0,0 +1,23 @@ +import { StringElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +export type { FallbackVisitorOptions as $refVisitorOptions }; + +/** + * @public + */ +class $refVisitor extends FallbackVisitor { + declare public readonly element: StringElement; + + StringElement(stringElement: StringElement) { + const result = super.enter(stringElement); + this.element.classes.push('reference-value'); + + return result; + } +} + +export default $refVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$vocabularyVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$vocabularyVisitor.ts new file mode 100644 index 0000000000..0cc743ba72 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/$vocabularyVisitor.ts @@ -0,0 +1,23 @@ +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +export type { FallbackVisitorOptions as $vocabularyVisitorOptions }; + +/** + * @public + */ +class $vocabularyVisitor extends FallbackVisitor { + declare public readonly element: ObjectElement; + + ObjectElement(objectElement: ObjectElement) { + const result = super.enter(objectElement); + this.element.classes.push('json-schema-$vocabulary'); + + return result; + } +} + +export default $vocabularyVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AllOfVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AllOfVisitor.ts new file mode 100644 index 0000000000..73454bb080 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AllOfVisitor.ts @@ -0,0 +1,45 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + SpecificationVisitor, + SpecificationVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface AllOfVisitorOptions + extends SpecificationVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AllOfVisitor extends Mixin(SpecificationVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ArrayElement; + + constructor(options: AllOfVisitorOptions) { + super(options); + this.element = new ArrayElement(); + this.element.classes.push('json-schema-allOf'); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default AllOfVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AnyOfVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AnyOfVisitor.ts new file mode 100644 index 0000000000..8e136c2fa6 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/AnyOfVisitor.ts @@ -0,0 +1,45 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + SpecificationVisitor, + SpecificationVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface AnyOfVisitorOptions + extends SpecificationVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class AnyOfVisitor extends Mixin(SpecificationVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ArrayElement; + + constructor(options: AnyOfVisitorOptions) { + super(options); + this.element = new ArrayElement(); + this.element.classes.push('json-schema-anyOf'); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default AnyOfVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentRequiredVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentRequiredVisitor.ts new file mode 100644 index 0000000000..0514bf7d00 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentRequiredVisitor.ts @@ -0,0 +1,23 @@ +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +export type { FallbackVisitorOptions as DependentRequiredVisitorOptions }; + +/** + * @public + */ +class DependentRequiredVisitor extends FallbackVisitor { + declare public readonly element: ObjectElement; + + ObjectElement(objectElement: ObjectElement) { + const result = super.enter(objectElement); + this.element.classes.push('json-schema-dependentRequired'); + + return result; + } +} + +export default DependentRequiredVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentSchemasVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentSchemasVisitor.ts new file mode 100644 index 0000000000..0b3c1b16e5 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/DependentSchemasVisitor.ts @@ -0,0 +1,38 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + MapVisitor, + MapVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface DependentSchemasVisitorOptions + extends MapVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class DependentSchemasVisitor extends Mixin(MapVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ObjectElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'JSONSchema']>; + + constructor(options: DependentSchemasVisitorOptions) { + super(options); + this.element = new ObjectElement(); + this.element.classes.push('json-schema-dependentSchemas'); + this.specPath = always(['document', 'objects', 'JSONSchema']); + } +} + +export default DependentSchemasVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/ItemsVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/ItemsVisitor.ts new file mode 100644 index 0000000000..a1d4ee13a7 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/ItemsVisitor.ts @@ -0,0 +1,60 @@ +import { Mixin } from 'ts-mixer'; +import { + ObjectElement, + ArrayElement, + BooleanElement, + Element, + BREAK, +} from '@swagger-api/apidom-core'; +import { + SpecificationVisitor, + SpecificationVisitorOptions, + FallbackVisitor, + FallbackVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface ItemsVisitorOptions + extends SpecificationVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class ItemsVisitor extends Mixin(SpecificationVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public element: ObjectElement | ArrayElement; + + ObjectElement(objectElement: ObjectElement) { + this.element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], objectElement); + + return BREAK; + } + + ArrayElement(arrayElement: ArrayElement) { + this.element = new ArrayElement(); + this.element.classes.push('json-schema-items'); + + arrayElement.forEach((item: Element): void => { + const element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } + + BooleanElement(booleanElement: BooleanElement) { + this.element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], booleanElement); + + return BREAK; + } +} + +export default ItemsVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/OneOfVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/OneOfVisitor.ts new file mode 100644 index 0000000000..0d3822dbe1 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/OneOfVisitor.ts @@ -0,0 +1,45 @@ +import { Mixin } from 'ts-mixer'; +import { ArrayElement, Element, BREAK } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + SpecificationVisitor, + SpecificationVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface OneOfVisitorOptions + extends SpecificationVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class OneOfVisitor extends Mixin(SpecificationVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ArrayElement; + + constructor(options: OneOfVisitorOptions) { + super(options); + this.element = new ArrayElement(); + this.element.classes.push('json-schema-oneOf'); + } + + ArrayElement(arrayElement: ArrayElement) { + arrayElement.forEach((item: Element): void => { + const element = this.toRefractedElement(['document', 'objects', 'JSONSchema'], item); + + this.element.push(element); + }); + + this.copyMetaAndAttributes(arrayElement, this.element); + + return BREAK; + } +} + +export default OneOfVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PatternPropertiesVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PatternPropertiesVisitor.ts new file mode 100644 index 0000000000..a2e372eb61 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PatternPropertiesVisitor.ts @@ -0,0 +1,42 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + MapVisitor, + MapVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface PatternPropertiesVisitorOptions + extends MapVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class PatternPropertiesVisitor extends Mixin( + MapVisitor, + ParentSchemaAwareVisitor, + FallbackVisitor, +) { + declare public readonly element: ObjectElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'JSONSchema']>; + + constructor(options: PatternPropertiesVisitorOptions) { + super(options); + this.element = new ObjectElement(); + this.element.classes.push('json-schema-patternProperties'); + this.specPath = always(['document', 'objects', 'JSONSchema']); + } +} + +export default PatternPropertiesVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PropertiesVisitor.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PropertiesVisitor.ts new file mode 100644 index 0000000000..cf1766fe14 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/PropertiesVisitor.ts @@ -0,0 +1,38 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import { + FallbackVisitor, + FallbackVisitorOptions, + MapVisitor, + MapVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +/** + * @public + */ +export interface PropertiesVisitorOptions + extends MapVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class PropertiesVisitor extends Mixin(MapVisitor, ParentSchemaAwareVisitor, FallbackVisitor) { + declare public readonly element: ObjectElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'JSONSchema']>; + + constructor(options: PropertiesVisitorOptions) { + super(options); + this.element = new ObjectElement(); + this.element.classes.push('json-schema-properties'); + this.specPath = always(['document', 'objects', 'JSONSchema']); + } +} + +export default PropertiesVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/index.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/index.ts new file mode 100644 index 0000000000..4500c6f6c6 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/index.ts @@ -0,0 +1,85 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { ObjectElement, BooleanElement, isStringElement } from '@swagger-api/apidom-core'; +import { + FixedFieldsVisitor, + FixedFieldsVisitorOptions, + ParentSchemaAwareVisitor, + ParentSchemaAwareVisitorOptions, + FallbackVisitor, + FallbackVisitorOptions, + SpecPath, + JSONSchemaVisitor as JSONSchemaDraft7Visitor, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +import JSONSchemaElement from '../../../elements/JSONSchema.ts'; + +/** + * @public + */ +export interface JSONSchemaVisitorOptions + extends FixedFieldsVisitorOptions, + ParentSchemaAwareVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class JSONSchemaVisitor extends Mixin( + FixedFieldsVisitor, + ParentSchemaAwareVisitor, + FallbackVisitor, +) { + declare public element: JSONSchemaElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'JSONSchema']>; + + constructor(options: JSONSchemaVisitorOptions) { + super(options); + this.specPath = always(['document', 'objects', 'JSONSchema']); + } + + // eslint-disable-next-line class-methods-use-this + get defaultDialectIdentifier(): string { + return 'https://json-schema.org/draft/2019-09/schema'; + } + + ObjectElement(objectElement: ObjectElement) { + this.element = new JSONSchemaElement(); + this.handleDialectIdentifier(objectElement); + this.handleSchemaIdentifier(objectElement); + + // for further processing consider this Schema Element as parent for all embedded Schema Elements + this.parent = this.element; + const result = FixedFieldsVisitor.prototype.ObjectElement.call(this, objectElement); + + // mark this SchemaElement with reference metadata + if (isStringElement(this.element.$ref)) { + this.element.classes.push('reference-element'); + this.element.setMetaProperty('referenced-element', 'schema'); + } + + return result; + } + + BooleanElement(booleanElement: BooleanElement) { + const result = this.enter(booleanElement); + this.element.classes.push('boolean-json-schema'); + + return result; + } + + handleDialectIdentifier(objectElement: ObjectElement): void { + return JSONSchemaDraft7Visitor.prototype.handleDialectIdentifier.call(this, objectElement); + } + + handleSchemaIdentifier(objectElement: ObjectElement, identifierKeyword: string = '$id'): void { + return JSONSchemaDraft7Visitor.prototype.handleSchemaIdentifier.call( + this, + objectElement, + identifierKeyword, + ); + } +} + +export default JSONSchemaVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/link-description/index.ts b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/link-description/index.ts new file mode 100644 index 0000000000..acf4b49935 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/refractor/visitors/json-schema/link-description/index.ts @@ -0,0 +1,35 @@ +import { Mixin } from 'ts-mixer'; +import { always } from 'ramda'; +import { + FixedFieldsVisitor, + FixedFieldsVisitorOptions, + FallbackVisitor, + FallbackVisitorOptions, + SpecPath, +} from '@swagger-api/apidom-ns-json-schema-draft-7'; + +import LinkDescriptionElement from '../../../../elements/LinkDescription.ts'; + +/** + * @public + */ +export interface LinkDescriptionVisitorOptions + extends FixedFieldsVisitorOptions, + FallbackVisitorOptions {} + +/** + * @public + */ +class LinkDescriptionVisitor extends Mixin(FixedFieldsVisitor, FallbackVisitor) { + declare public readonly element: LinkDescriptionElement; + + declare protected readonly specPath: SpecPath<['document', 'objects', 'LinkDescription']>; + + constructor(options: LinkDescriptionVisitorOptions) { + super(options); + this.element = new LinkDescriptionElement(); + this.specPath = always(['document', 'objects', 'LinkDescription']); + } +} + +export default LinkDescriptionVisitor; diff --git a/packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts b/packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts new file mode 100644 index 0000000000..db96a79063 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/src/traversal/visitor.ts @@ -0,0 +1,20 @@ +import { keyMap as keyMapBase, isElement, Element } from '@swagger-api/apidom-core'; + +/** + * @public + */ +export const getNodeType = (element: T): string | undefined => { + if (!isElement(element)) { + return undefined; + } + return `${element.element.charAt(0).toUpperCase() + element.element.slice(1)}Element`; +}; + +/** + * @public + */ +export const keyMap = { + JSONSchema201909Element: ['content'], + LinkDescriptionElement: ['content'], + ...keyMapBase, +}; diff --git a/packages/apidom-ns-json-schema-2019-09/test/.eslintrc b/packages/apidom-ns-json-schema-2019-09/test/.eslintrc new file mode 100644 index 0000000000..5cc99718c6 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/.eslintrc @@ -0,0 +1,57 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], + "rules": { + "no-void": 0, + "no-underscore-dangle": 0, + "func-names": 0, + "prefer-arrow-callback": 0, + "no-array-constructor": 0, + "prefer-rest-params": 0, + "no-new-wrappers": 0, + "mocha/no-skipped-tests": 2, + "mocha/handle-done-callback": 2, + "mocha/valid-suite-description": 2, + "mocha/no-mocha-arrows": 2, + "mocha/no-hooks-for-single-case": 2, + "mocha/no-sibling-hooks": 2, + "mocha/no-top-level-hooks": 2, + "mocha/no-identical-title": 2, + "mocha/no-nested-tests": 2, + "mocha/no-exclusive-tests": 2, + "max-classes-per-file": 0, + "@typescript-eslint/no-unused-expressions": 0, + "import/no-relative-packages": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "variable", + "format": ["camelCase", "PascalCase", "UPPER_CASE"], + "leadingUnderscore": "forbid" + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__dirname$", + "match": true + } + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__filename$", + "match": true + } + } + ] + } +} diff --git a/packages/apidom-ns-json-schema-2019-09/test/mocha-bootstrap.ts b/packages/apidom-ns-json-schema-2019-09/test/mocha-bootstrap.ts new file mode 100644 index 0000000000..aec560d03f --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/mocha-bootstrap.ts @@ -0,0 +1,11 @@ +import * as chai from 'chai'; +import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + +// @ts-ignore +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-ns-json-schema-2019-09/test/predicates.ts b/packages/apidom-ns-json-schema-2019-09/test/predicates.ts new file mode 100644 index 0000000000..f7dba873bd --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/predicates.ts @@ -0,0 +1,120 @@ +import { assert } from 'chai'; + +import { + isJSONSchemaElement, + isLinkDescriptionElement, + JSONSchemaElement, + LinkDescriptionElement, +} from '../src/index.ts'; + +describe('predicates', function () { + context('isJSONSchemaElement', function () { + context('given JSONSchemaElement instance value', function () { + specify('should return true', function () { + const element = new JSONSchemaElement(); + + assert.isTrue(isJSONSchemaElement(element)); + }); + }); + + context('given subtype instance value', function () { + specify('should return true', function () { + // eslint-disable-next-line @typescript-eslint/naming-convention + class JSONSchemaSubElement extends JSONSchemaElement {} + + assert.isTrue(isJSONSchemaElement(new JSONSchemaSubElement())); + }); + }); + + context('given non JSONSchema instance value', function () { + specify('should return false', function () { + assert.isFalse(isJSONSchemaElement(1)); + assert.isFalse(isJSONSchemaElement(null)); + assert.isFalse(isJSONSchemaElement(undefined)); + assert.isFalse(isJSONSchemaElement({})); + assert.isFalse(isJSONSchemaElement([])); + assert.isFalse(isJSONSchemaElement('string')); + }); + }); + + specify('should support duck-typing', function () { + const jsonSchemaElementDuck = { + _storedElement: 'JSONSchema201909', + _content: [], + primitive() { + return 'object'; + }, + get element() { + return this._storedElement; + }, + }; + + const jsonSchemaElementSwan = { + _storedElement: undefined, + _content: undefined, + primitive() { + return 'swan'; + }, + get length() { + return 0; + }, + }; + + assert.isTrue(isJSONSchemaElement(jsonSchemaElementDuck)); + assert.isFalse(isJSONSchemaElement(jsonSchemaElementSwan)); + }); + }); + + context('isLinkDescriptionElement', function () { + context('given LinkDescriptionElement instance value', function () { + specify('should return true', function () { + const element = new LinkDescriptionElement(); + + assert.isTrue(isLinkDescriptionElement(element)); + }); + }); + + context('given subtype instance value', function () { + specify('should return true', function () { + class LinkDescriptionSubElement extends LinkDescriptionElement {} + + assert.isTrue(isLinkDescriptionElement(new LinkDescriptionSubElement())); + }); + }); + + context('given non LinkDescriptionElement instance value', function () { + specify('should return false', function () { + assert.isFalse(isLinkDescriptionElement(1)); + assert.isFalse(isLinkDescriptionElement(null)); + assert.isFalse(isLinkDescriptionElement(undefined)); + assert.isFalse(isLinkDescriptionElement({})); + assert.isFalse(isLinkDescriptionElement([])); + assert.isFalse(isLinkDescriptionElement('string')); + }); + }); + + specify('should support duck-typing', function () { + const linkDescriptionElementDuck = { + _storedElement: 'linkDescription', + _content: '', + primitive() { + return 'object'; + }, + get element() { + return this._storedElement; + }, + }; + + const linkDescriptionElementSwan = { + _storedElement: undefined, + _content: undefined, + primitive() { + return 'swan'; + }, + }; + + assert.isTrue(isLinkDescriptionElement(linkDescriptionElementDuck)); + assert.isFalse(isLinkDescriptionElement(linkDescriptionElementSwan)); + }); + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/__snapshots__/index.mjs.snap b/packages/apidom-ns-json-schema-2019-09/test/refractor/__snapshots__/index.mjs.snap new file mode 100644 index 0000000000..46a65a493f --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/__snapshots__/index.mjs.snap @@ -0,0 +1,1762 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor given generic ApiDOM object in JSON Schema 2019-09 shape should refract to JSONSchema Element 1`] = ` +{ + "element": "JSONSchema201909", + "meta": { + "ancestorsSchemaIdentifiers": { + "element": "array", + "content": [ + { + "element": "string", + "content": "http://x.y.z/rootschema.json#" + } + ] + }, + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "reference-element" + } + ] + }, + "referenced-element": { + "element": "string", + "content": "schema" + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$id" + }, + "value": { + "element": "string", + "content": "http://x.y.z/rootschema.json#" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$schema" + }, + "value": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "multipleOf" + }, + "value": { + "element": "number", + "content": 1 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "maximum" + }, + "value": { + "element": "number", + "content": 2 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "exclusiveMaximum" + }, + "value": { + "element": "number", + "content": 3 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "minimum" + }, + "value": { + "element": "number", + "content": 4 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "exclusiveMinimum" + }, + "value": { + "element": "number", + "content": 5 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "maxLength" + }, + "value": { + "element": "number", + "content": 6 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "minLength" + }, + "value": { + "element": "number", + "content": 7 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "pattern" + }, + "value": { + "element": "string", + "content": "[a-z]+" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "additionalItems" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "items" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-items" + } + ] + } + }, + "content": [ + { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "maxItems" + }, + "value": { + "element": "number", + "content": 8 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "minItems" + }, + "value": { + "element": "number", + "content": 9 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "uniqueItems" + }, + "value": { + "element": "boolean", + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "contains" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "maxProperties" + }, + "value": { + "element": "number", + "content": 10 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "minProperties" + }, + "value": { + "element": "number", + "content": 11 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "required" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-required" + } + ] + } + }, + "content": [ + { + "element": "string", + "content": "prop1" + }, + { + "element": "string", + "content": "prop2" + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "properties" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-properties" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "prop1" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "additionalProperties" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "patternProperties" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-patternProperties" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "[a-z]+" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "dependentSchemas" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-dependentSchemas" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "dep1" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "propertyNames" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "enum" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-enum" + } + ] + } + }, + "content": [ + { + "element": "number", + "content": 1 + }, + { + "element": "string", + "content": "2" + }, + { + "element": "null", + "content": null + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "const" + }, + "value": { + "element": "number", + "content": 1 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "type" + }, + "value": { + "element": "string", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-type" + } + ] + } + }, + "content": "string" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "allOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-allOf" + } + ] + } + }, + "content": [ + { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "anyOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-anyOf" + } + ] + } + }, + "content": [ + { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "oneOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-oneOf" + } + ] + } + }, + "content": [ + { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "not" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$defs" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-$defs" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "def1" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "title" + }, + "value": { + "element": "string", + "content": "title" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "description" + }, + "value": { + "element": "string", + "content": "description" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "default" + }, + "value": { + "element": "number", + "content": 3 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "examples" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-examples" + } + ] + } + }, + "content": [ + { + "element": "number", + "content": 1 + }, + { + "element": "string", + "content": "string" + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "format" + }, + "value": { + "element": "string", + "content": "url" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "base" + }, + "value": { + "element": "string", + "content": "/object/{id}" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "links" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-links" + } + ] + } + }, + "content": [ + { + "element": "linkDescription" + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "readOnly" + }, + "value": { + "element": "boolean", + "content": false + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$vocabulary" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-$vocabulary" + } + ] + } + }, + "content": [ + { + "element": "member", + "content": { + "key": { + "element": "string", + "content": "http://example.com/vocab#" + }, + "value": { + "element": "boolean", + "content": true + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$anchor" + }, + "value": { + "element": "string", + "content": "anchor" + } + } + }, + { + "element": "member", + "content": { + "key": { + "element": "string", + "content": "recursiveAnchor" + }, + "value": { + "element": "boolean", + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$ref" + }, + "value": { + "element": "string", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "reference-value" + } + ] + } + }, + "content": "#anchor" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$recursiveRef" + }, + "value": { + "element": "string", + "content": "#anchor" + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "unevaluatedItems" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "unevaluatedProperties" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "maxContains" + }, + "value": { + "element": "number", + "content": 3 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "minContains" + }, + "value": { + "element": "number", + "content": 1 + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "dependentRequired" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-dependentRequired" + } + ] + } + }, + "content": [ + { + "element": "member", + "content": { + "key": { + "element": "string", + "content": "prop1" + }, + "value": { + "element": "array", + "content": [ + { + "element": "string", + "content": "prop2" + } + ] + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "deprecated" + }, + "value": { + "element": "boolean", + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "contentSchema" + }, + "value": { + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + } + } + } + } + ] +} +`; diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/__snapshots__/index.mjs.snap b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/__snapshots__/index.mjs.snap new file mode 100644 index 0000000000..de0ae34bb6 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/__snapshots__/index.mjs.snap @@ -0,0 +1,1172 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements Boolean JSON Schema support should refract to semantic ApiDOM tree 1`] = ` +{ + "element": "JSONSchema201909", + "meta": { + "inheritedDialectIdentifier": { + "element": "string", + "content": "https://json-schema.org/draft/2019-09/schema" + }, + "ancestorsSchemaIdentifiers": { + "element": "array" + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "additionalItems" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "items" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "contains" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "additionalProperties" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": false + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "patternProperties" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-patternProperties" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "[a-z]+" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "dependentSchemas" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-dependentSchemas" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "dep1" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": false + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "propertyNames" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "if" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "then" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": false + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "else" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "allOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-allOf" + } + ] + } + }, + "content": [ + { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "anyOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-anyOf" + } + ] + } + }, + "content": [ + { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "oneOf" + }, + "value": { + "element": "array", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-oneOf" + } + ] + } + }, + "content": [ + { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": false + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "not" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": false + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "$defs" + }, + "value": { + "element": "object", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "json-schema-$defs" + } + ] + } + }, + "content": [ + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "patterned-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "def1" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + } + ] + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "unevaluatedItems" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "unevaluatedProperties" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + }, + { + "element": "member", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "fixed-field" + } + ] + } + }, + "content": { + "key": { + "element": "string", + "content": "contentSchema" + }, + "value": { + "element": "boolean", + "meta": { + "classes": { + "element": "array", + "content": [ + { + "element": "string", + "content": "boolean-json-schema" + } + ] + } + }, + "content": true + } + } + } + ] +} +`; + +exports[`refractor elements JSONSchema should refract to semantic ApiDOM tree 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element))) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (ArrayElement + (NumberElement) + (StringElement) + (NullElement))) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element))) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (ArrayElement + (NumberElement) + (StringElement))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement))) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (BooleanElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement))))) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`refractor elements JSONSchema with alternate field values should refract to semantic ApiDOM tree 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement))) +`; + +exports[`refractor elements given $ref fields should refract to semantic ApiDOM tree 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement))))))) +`; + +exports[`refractor elements given generic ApiDOM element should refract to semantic ApiDOM tree 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (ObjectElement))) +`; diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/index.ts b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/index.ts new file mode 100644 index 0000000000..41a15ab11e --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/JSONSchema/index.ts @@ -0,0 +1,171 @@ +import { assert, expect } from 'chai'; +import { ObjectElement, sexprs, toValue } from '@swagger-api/apidom-core'; + +import { JSONSchemaElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('JSONSchema', function () { + specify('should refract to semantic ApiDOM tree', function () { + const jsonSchemaElement = JSONSchemaElement.refract({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + $comment: 'this is comment', + multipleOf: 1, + maximum: 2, + exclusiveMaximum: 3, + minimum: 4, + exclusiveMinimum: 5, + maxLength: 6, + minLength: 7, + pattern: '[a-z]+', + additionalItems: {}, + items: [{}], + maxItems: 8, + minItems: 9, + uniqueItems: true, + contains: {}, + maxProperties: 10, + minProperties: 11, + required: ['prop1', 'prop2'], + properties: { prop1: {} }, + additionalProperties: {}, + patternProperties: { '[a-z]+': {} }, + dependentSchemas: { dep1: {} }, + propertyNames: {}, + if: {}, + then: {}, + else: {}, + enum: [1, '2', null], + const: 1, + type: 'string', + allOf: [{}], + anyOf: [{}], + oneOf: [{}], + not: {}, + $defs: { def1: {} }, + contentEncoding: 'base64', + contentMediaType: 'image/png', + title: 'title', + description: 'description', + default: 3, + examples: [1, 'string'], + format: 'url', + base: '/object/{id}', + links: [{}], + readOnly: false, + writeOnly: true, + $vocabulary: { 'http://example.com/vocab#': true }, + $anchor: 'anchor', + recursiveAnchor: true, + $ref: '#anchor', + $recursiveRef: '#anchor', + unevaluatedItems: {}, + unevaluatedProperties: {}, + maxContains: 3, + minContains: 1, + dependentRequired: { prop1: ['prop2'] }, + deprecated: true, + contentSchema: {}, + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); + }); + + context('JSONSchema with alternate field values', function () { + specify('should refract to semantic ApiDOM tree', function () { + const jsonSchemaElement = JSONSchemaElement.refract({ + additionalItems: true, + items: {}, + contains: true, + additionalProperties: true, + propertyNames: true, + type: ['string', 'number'], + unevaluatedItems: true, + unevaluatedProperties: true, + contentSchema: true, + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); + }); + + context('Boolean JSON Schema support', function () { + specify('should refract to semantic ApiDOM tree', function () { + const jsonSchemaElement = JSONSchemaElement.refract({ + additionalItems: true, + items: true, + contains: true, + additionalProperties: false, + patternProperties: { '[a-z]+': true }, + dependentSchemas: { dep1: false }, + propertyNames: true, + if: true, + then: false, + else: true, + allOf: [true], + anyOf: [true], + oneOf: [false], + not: false, + $defs: { def1: true }, + unevaluatedItems: true, + unevaluatedProperties: true, + contentSchema: true, + }); + + expect(jsonSchemaElement).toMatchSnapshot(); + }); + }); + + context('given $ref fields', function () { + specify('should refract to semantic ApiDOM tree', function () { + const jsonSchemaElement = JSONSchemaElement.refract({ + additionalItems: { $ref: '#/json/pointer' }, + items: [{ $ref: '#/json/pointer' }], + contains: { $ref: '#/json/pointer' }, + properties: { prop1: { $ref: '#/json/pointer' } }, + additionalProperties: { $ref: '#/json/pointer' }, + patternProperties: { '[a-z]+': { $ref: '#/json/pointer' } }, + dependentSchemas: { dep1: { $ref: '#/json/pointer' } }, + propertyNames: { $ref: '#/json/pointer' }, + if: { $ref: '#/json/pointer' }, + then: { $ref: '#/json/pointer' }, + else: { $ref: '#/json/pointer' }, + allOf: [{ $ref: '#/json/pointer' }], + anyOf: [{ $ref: '#/json/pointer' }], + oneOf: [{ $ref: '#/json/pointer' }], + not: { $ref: '#/json/pointer' }, + $defs: { def1: { $ref: '#/json/pointer' } }, + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); + }); + + context('given generic ApiDOM element', function () { + let jsonSchemaElement: JSONSchemaElement; + + beforeEach(function () { + const propertiesKeyword = new ObjectElement({}, { classes: ['example'] }, { attr: true }); + jsonSchemaElement = JSONSchemaElement.refract( + new ObjectElement({ properties: propertiesKeyword }), + ) as JSONSchemaElement; + }); + + specify('should refract to semantic ApiDOM tree', function () { + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); + + specify('should deepmerge meta', function () { + assert.deepEqual(toValue(jsonSchemaElement.properties!.meta), { + classes: ['json-schema-properties', 'example'], + }); + }); + + specify('should deepmerge attributes', function () { + assert.isTrue(jsonSchemaElement.properties!.attributes.get('attr').equals(true)); + }); + }); + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/__snapshots__/index.mjs.snap b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/__snapshots__/index.mjs.snap new file mode 100644 index 0000000000..77225aab6e --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/__snapshots__/index.mjs.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`refractor elements LinkDescription should refract to semantic ApiDOM tree 1`] = ` +(LinkDescriptionElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (StringElement))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ArrayElement)) + (MemberElement + (StringElement) + (ArrayElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/index.ts b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/index.ts new file mode 100644 index 0000000000..f2914c4153 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/elements/LinkDescription/index.ts @@ -0,0 +1,39 @@ +import { expect } from 'chai'; +import { sexprs } from '@swagger-api/apidom-core'; + +import { LinkDescriptionElement } from '../../../../src/index.ts'; + +describe('refractor', function () { + context('elements', function () { + context('LinkDescription', function () { + specify('should refract to semantic ApiDOM tree', function () { + const linkDescriptionElement = LinkDescriptionElement.refract({ + $comment: 'this is comment', + anchor: 'nodes/{thisNodeId}', + anchorPointer: '#/relative/json/pointer', + templatePointers: { + pointer1: '#/relative/json/pointer/1', + pointer2: '#/relative/json/pointer/2', + }, + templateRequired: ['id'], + href: 'things/{id}', + hrefSchema: {}, + headerSchema: {}, + rel: 'image/png', + title: 'title', + targetSchema: {}, + targetMediaType: 'text/html', + targetHints: { + field1: [], + field2: [], + }, + description: 'LDO description', + submissionMediaType: 'multipart/alternative; boundary=ab2', + submissionSchema: {}, + }); + + expect(sexprs(linkDescriptionElement)).toMatchSnapshot(); + }); + }); + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/fixtures/json-schema-2019-09.json b/packages/apidom-ns-json-schema-2019-09/test/refractor/fixtures/json-schema-2019-09.json new file mode 100644 index 0000000000..035dfdaf9f --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/fixtures/json-schema-2019-09.json @@ -0,0 +1,62 @@ +{ + "$id": "http://x.y.z/rootschema.json#", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "multipleOf": 1, + "maximum": 2, + "exclusiveMaximum": 3, + "minimum": 4, + "exclusiveMinimum": 5, + "maxLength": 6, + "minLength": 7, + "pattern": "[a-z]+", + "additionalItems": {}, + "items": [{}], + "maxItems": 8, + "minItems": 9, + "uniqueItems": true, + "contains": {}, + "maxProperties": 10, + "minProperties": 11, + "required": ["prop1", "prop2"], + "properties": { + "prop1": {} + }, + "additionalProperties": {}, + "patternProperties": { + "[a-z]+": {} + }, + "dependentSchemas": { + "dep1": {} + }, + "propertyNames": {}, + "enum": [1, "2", null], + "const": 1, + "type": "string", + "allOf": [{}], + "anyOf": [{}], + "oneOf": [{}], + "not": {}, + "$defs": { + "def1": {} + }, + "title": "title", + "description": "description", + "default": 3, + "examples": [1, "string"], + "format": "url", + "base": "/object/{id}", + "links": [{}], + "readOnly": false, + "$vocabulary": { "http://example.com/vocab#": true }, + "$anchor": "anchor", + "recursiveAnchor": true, + "$ref": "#anchor", + "$recursiveRef": "#anchor", + "unevaluatedItems": {}, + "unevaluatedProperties": {}, + "maxContains": 3, + "minContains": 1, + "dependentRequired": { "prop1": ["prop2"] }, + "deprecated": true, + "contentSchema": {} +} diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/index.ts b/packages/apidom-ns-json-schema-2019-09/test/refractor/index.ts new file mode 100644 index 0000000000..96a5c17234 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/index.ts @@ -0,0 +1,317 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert, expect } from 'chai'; +import sinon from 'sinon'; +import { ObjectElement, toValue, Namespace } from '@swagger-api/apidom-core'; + +import { + JSONSchemaElement, + LinkDescriptionElement, + isLinkDescriptionElement, +} from '../../src/index.ts'; +import * as predicates from '../../src/predicates.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('refractor', function () { + context('given generic ApiDOM object in JSON Schema 2019-09 shape', function () { + specify('should refract to JSONSchema Element', function () { + const jsonSchemaString = fs + .readFileSync(path.join(__dirname, 'fixtures', 'json-schema-2019-09.json')) + .toString(); + const jsonSchemaPojo = JSON.parse(jsonSchemaString); + const genericObjectElement = new ObjectElement(jsonSchemaPojo); + const jsonSchemaElement = JSONSchemaElement.refract(genericObjectElement); + + expect(jsonSchemaElement).toMatchSnapshot(); + }); + }); + + context('supports plugins', function () { + let plugin1Spec: any; + let plugin2Spec: any; + let plugin1: any; + let plugin2: any; + + beforeEach(function () { + plugin1Spec = { + name: 'plugin1', + pre() {}, + visitor: { + LinkDescriptionElement(element: LinkDescriptionElement) { + // @ts-ignore + element.anchor = 'nodes/{thisNodeId}'; // eslint-disable-line no-param-reassign + }, + }, + post() {}, + }; + plugin2Spec = { + name: 'plugin2', + pre() {}, + visitor: { + LinkDescriptionElement(element: LinkDescriptionElement) { + // @ts-ignore + element.meta.set('metaKey', 'metaValue'); + }, + }, + post() {}, + }; + plugin1 = sinon.spy(() => plugin1Spec); + plugin2 = sinon.spy(() => plugin2Spec); + + sinon.spy(plugin1Spec, 'pre'); + sinon.spy(plugin1Spec, 'post'); + sinon.spy(plugin1Spec.visitor, 'LinkDescriptionElement'); + + sinon.spy(plugin2Spec, 'pre'); + sinon.spy(plugin2Spec, 'post'); + sinon.spy(plugin2Spec.visitor, 'LinkDescriptionElement'); + }); + + context('plugin', function () { + specify('should be called with toolbox object', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.hasAllKeys(plugin1.firstCall.args[0], ['predicates', 'namespace']); + }); + + specify('should have predicates in toolbox object', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.hasAnyKeys(plugin1.firstCall.args[0].predicates, Object.keys(predicates)); + }); + + specify('should have namespace in toolbox object', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.instanceOf(plugin1.firstCall.args[0].namespace, Namespace); + }); + }); + + context('pre hook', function () { + specify('should call it once', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.isTrue(plugin1Spec.pre.calledOnce); + }); + + specify('should call it before other plugin pre hook', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue(plugin1Spec.pre.calledBefore(plugin2Spec.pre)); + }); + + specify('should call it before visiting', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue(plugin1Spec.pre.calledBefore(plugin1Spec.visitor.LinkDescriptionElement)); + assert.isTrue(plugin1Spec.pre.calledBefore(plugin2Spec.visitor.LinkDescriptionElement)); + }); + }); + + context('post hook', function () { + specify('should call it once', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.isTrue(plugin1Spec.post.calledOnce); + }); + + specify('should call it before other plugin post hook', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue(plugin1Spec.post.calledBefore(plugin2Spec.post)); + }); + + specify('should call it after visiting', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue(plugin1Spec.post.calledAfter(plugin1Spec.visitor.LinkDescriptionElement)); + assert.isTrue(plugin1Spec.post.calledAfter(plugin2Spec.visitor.LinkDescriptionElement)); + }); + }); + + context('visitor', function () { + specify('should be called once', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue(plugin1Spec.visitor.LinkDescriptionElement.calledOnce); + assert.isTrue(plugin2Spec.visitor.LinkDescriptionElement.calledOnce); + }); + + specify('should be called in proper order', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue( + plugin1Spec.visitor.LinkDescriptionElement.calledBefore( + plugin2Spec.visitor.LinkDescriptionElement, + ), + ); + }); + + context('first plugin', function () { + specify('should receive arguments', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.lengthOf(plugin1Spec.visitor.LinkDescriptionElement.firstCall.args, 6); + }); + + specify('should receive node as first argument', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.isTrue( + isLinkDescriptionElement(plugin1Spec.visitor.LinkDescriptionElement.firstCall.args[0]), + ); + }); + + specify('should augment LinkDescriptionElement.anchor field', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + const jsonSchemaElement = JSONSchemaElement.refract(genericObject, { + plugins: [plugin1], + }); + + assert.deepEqual(toValue(jsonSchemaElement), { + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{ anchor: 'nodes/{thisNodeId}' }], + }); + }); + }); + + context('second plugin', function () { + specify('should receive arguments', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.lengthOf(plugin2Spec.visitor.LinkDescriptionElement.firstCall.args, 6); + }); + + specify('should receive node as first argument', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.isTrue( + isLinkDescriptionElement(plugin2Spec.visitor.LinkDescriptionElement.firstCall.args[0]), + ); + }); + + specify('should append metadata to LinkDescriptionElement', function () { + const genericObject = new ObjectElement({ + $id: 'http://x.y.z/rootschema.json#', + $schema: 'https://json-schema.org/draft/2019-09/schema', + links: [{}], + }); + const jsonSchemaElement = JSONSchemaElement.refract(genericObject, { + plugins: [plugin1, plugin2], + }); + + assert.strictEqual( + // @ts-ignore + toValue(jsonSchemaElement.links.get(0).meta.get('metaKey')), + 'metaValue', + ); + }); + }); + }); + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/mappings.mjs.snap b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/mappings.mjs.snap new file mode 100644 index 0000000000..3a30410c9e --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/mappings.mjs.snap @@ -0,0 +1,241 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`given JSON Schema definition with no empty values should do nothing 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element))))) +`; + +exports[`given empty value for $dependentRequired field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; + +exports[`given empty value for $vocabulary field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; + +exports[`given empty value for LinkDescription.headerSchema field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))))) +`; + +exports[`given empty value for LinkDescription.hrefSchema field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))))) +`; + +exports[`given empty value for LinkDescription.submissionSchema field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))))) +`; + +exports[`given empty value for LinkDescription.targetHints field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (ObjectElement)))))) +`; + +exports[`given empty value for LinkDescription.targetSchema field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (JSONSchema201909Element)))))) +`; + +exports[`given empty value for LinkDescription.templatePointers field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (ObjectElement)))))) +`; + +exports[`given empty value for contentSchema field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for else field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for field additionalItems should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for field enum should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement))) +`; + +exports[`given empty value for field patternProperties should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement))) +`; + +exports[`given empty value for if field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for propertyNames field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for then field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for unevaluatedItems field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value for unevaluatedProperties field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value instead for contains field keys should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (JSONSchema201909Element))) +`; + +exports[`given empty value instead for properties field keys should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (JSONSchema201909Element))))) +`; diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/sequences.mjs.snap b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/sequences.mjs.snap new file mode 100644 index 0000000000..d5f6a32121 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/__snapshots__/sequences.mjs.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`given JSON Schema definition with no empty values should do nothing 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element) + (JSONSchema201909Element)))) +`; + +exports[`given empty value for LinkDescription.templateRequired field should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (LinkDescriptionElement + (MemberElement + (StringElement) + (ArrayElement)))))) +`; + +exports[`given empty value for field allOf should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element)))) +`; + +exports[`given empty value for field anyOf should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element) + (JSONSchema201909Element)))) +`; + +exports[`given empty value for field examples should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement))) +`; + +exports[`given empty value for field oneOf should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (JSONSchema201909Element) + (JSONSchema201909Element)))) +`; + +exports[`given empty value for field type should replace empty value with semantic element 1`] = ` +(JSONSchema201909Element + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement))) +`; diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/mappings.ts b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/mappings.ts new file mode 100644 index 0000000000..fb3764ce1e --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/mappings.ts @@ -0,0 +1,350 @@ +import { expect } from 'chai'; +import dedent from 'dedent'; +import { sexprs, SourceMapElement } from '@swagger-api/apidom-core'; +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; + +import { refractorPluginReplaceEmptyElement, JSONSchemaElement } from '../../../../src/index.ts'; + +describe('given empty value for field additionalItems', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + additionalItems: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field patternProperties', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + patternProperties: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field enum', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + enum: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value instead for properties field keys', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + properties: + prop1: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value instead for contains field keys', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + contains: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for propertyNames field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + propertyNames: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for if field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + if: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for then field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + then: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for else field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + else: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for $vocabulary field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + $vocabulary: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for unevaluatedItems field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + unevaluatedItems: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for unevaluatedProperties field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + unevaluatedProperties: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for $dependentRequired field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + $dependentRequired: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for contentSchema field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + contentSchema: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.hrefSchema field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - hrefSchema: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.targetSchema field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - targetSchema: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.submissionSchema field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - submissionSchema: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.templatePointers field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - templatePointers: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.targetHints field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - targetHints: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.headerSchema field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - headerSchema: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given JSON Schema definition with no empty values', function () { + it('should do nothing', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + properties: + prop1: {} + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given JSON Schema definition with empty values', function () { + it('should generate proper source maps', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + properties: + `; + const apiDOM = await parse(yamlDefinition, { sourceMap: true }); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + const { properties: propertiesValue } = jsonSchemaElement; + const sourceMap = propertiesValue?.meta.get('sourceMap'); + const { positionStart, positionEnd } = sourceMap; + const expectedPosition = [1, 11, 67]; + + expect(propertiesValue?.meta.get('sourceMap')).to.be.an.instanceof(SourceMapElement); + expect(positionStart.equals(expectedPosition)).to.be.true; + expect(positionEnd.equals(expectedPosition)).to.be.true; + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/sequences.ts b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/sequences.ts new file mode 100644 index 0000000000..f1b53d68ac --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/refractor/plugins/replace-empty-element/sequences.ts @@ -0,0 +1,141 @@ +import { expect } from 'chai'; +import dedent from 'dedent'; +import { sexprs, SourceMapElement } from '@swagger-api/apidom-core'; +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; + +import { refractorPluginReplaceEmptyElement, JSONSchemaElement } from '../../../../src/index.ts'; + +describe('given empty value for field allOf', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + allOf: + - + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchema = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchema)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field anyOf', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + anyOf: + - + - + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchema = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchema)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field oneOf', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + oneOf: + - + - + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchema = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchema)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field examples', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + examples: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchema = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchema)).toMatchSnapshot(); + }); +}); + +describe('given empty value for field type', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + type: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchema = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }); + + expect(sexprs(jsonSchema)).toMatchSnapshot(); + }); +}); + +describe('given empty value for LinkDescription.templateRequired field', function () { + it('should replace empty value with semantic element', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + links: + - templateRequired: + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given JSON Schema definition with no empty values', function () { + it('should do nothing', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + oneOf: + - {} + - {} + `; + const apiDOM = await parse(yamlDefinition); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + + expect(sexprs(jsonSchemaElement)).toMatchSnapshot(); + }); +}); + +describe('given JSON Schema definition with empty values', function () { + it('should generate proper source maps', async function () { + const yamlDefinition = dedent` + $schema: 'https://json-schema.org/draft/2019-09/schema' + oneOf: + - + `; + const apiDOM = await parse(yamlDefinition, { sourceMap: true }); + const jsonSchemaElement = JSONSchemaElement.refract(apiDOM.result, { + plugins: [refractorPluginReplaceEmptyElement()], + }) as JSONSchemaElement; + const { oneOf: oneOfValue } = jsonSchemaElement; + const sourceMap = oneOfValue?.get(0)?.meta.get('sourceMap'); + const { positionStart, positionEnd } = sourceMap; + const expectedPosition = [2, 2, 65]; + + expect(oneOfValue?.meta.get('sourceMap')).to.be.an.instanceof(SourceMapElement); + expect(positionStart.equals(expectedPosition)).to.be.true; + expect(positionEnd.equals(expectedPosition)).to.be.true; + }); +}); diff --git a/packages/apidom-ns-json-schema-2019-09/test/tsconfig.json b/packages/apidom-ns-json-schema-2019-09/test/tsconfig.json new file mode 100644 index 0000000000..405aae2d2f --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + }, + "include": [ + "." + ] +} diff --git a/packages/apidom-ns-json-schema-2019-09/tsconfig.declaration.json b/packages/apidom-ns-json-schema-2019-09/tsconfig.declaration.json new file mode 100644 index 0000000000..82d128fa80 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/tsconfig.declaration.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-ns-json-schema-2019-09/tsconfig.json b/packages/apidom-ns-json-schema-2019-09/tsconfig.json new file mode 100644 index 0000000000..5cc50cd885 --- /dev/null +++ b/packages/apidom-ns-json-schema-2019-09/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ] +}