diff --git a/apidom/packages/apidom/src/index.ts b/apidom/packages/apidom/src/index.ts index 70e89f2da6..9008d285d1 100644 --- a/apidom/packages/apidom/src/index.ts +++ b/apidom/packages/apidom/src/index.ts @@ -111,3 +111,5 @@ export const toString = ( const refract = dehydrate(element, namespace); return JSON.stringify(refract); }; + +export { default as sexprs } from './sexprs'; diff --git a/apidom/packages/apidom/src/sexprs.ts b/apidom/packages/apidom/src/sexprs.ts new file mode 100644 index 0000000000..e503390259 --- /dev/null +++ b/apidom/packages/apidom/src/sexprs.ts @@ -0,0 +1,34 @@ +import stampit from 'stampit'; +import { Element } from 'minim'; + +import { visit } from './traversal/visitor'; + +const SymbolicExpressionsVisitor = stampit({ + props: { + nestingLevel: 0, + result: '', + }, + methods: { + enter(element: Element) { + const { element: elementName } = element; + const capitalizedElementName = elementName.charAt(0).toUpperCase() + elementName.slice(1); + const indent = ' '.repeat(this.nestingLevel); + this.result += this.nestingLevel > 0 ? '\n' : ''; + this.result += `${indent}(${capitalizedElementName}Element`; + this.nestingLevel += 1; + }, + leave() { + this.nestingLevel -= 1; + this.result += ')'; + }, + }, +}); + +// transforms ApiDOM into S-expressions (Symbolic Expressions) +const sexprs = (element: Element): string => { + const visitor = SymbolicExpressionsVisitor(); + visit(element, visitor); + return visitor.result; +}; + +export default sexprs; diff --git a/apidom/packages/apidom/test/sexprs.ts b/apidom/packages/apidom/test/sexprs.ts new file mode 100644 index 0000000000..6579400cf6 --- /dev/null +++ b/apidom/packages/apidom/test/sexprs.ts @@ -0,0 +1,95 @@ +import { assert } from 'chai'; +import { ObjectElement } from 'minim'; +import dedent from 'dedent'; +import { trim } from 'ramda'; +import { InfoElement } from 'apidom-ns-openapi-3-1'; + +import sexprs from '../src/sexprs'; + +describe('sexprs', function () { + context('given generic ApiDOM', function () { + specify('should transform into S-expressions', function () { + const genericObj = new ObjectElement({ + a: 1, + b: true, + c: ['a', null], + }); + const expected = trim(dedent` + (ObjectElement + (MemberElement + (StringElement) + (NumberElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (NullElement))))`); + + assert.strictEqual(sexprs(genericObj), expected); + }); + }); + + context('given semantic ApiDOM', function () { + specify('should transform into S-expressions', function () { + const semanticObj = InfoElement.refract({ + title: 'title', + summary: 'summary', + description: 'description', + contact: { + name: 'name', + url: 'url', + email: 'email', + }, + license: { + name: 'name', + identifier: 'identifier', + url: 'url', + }, + version: '1.0.0', + }); + const expected = trim(dedent` + (InfoElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ContactElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (LicenseElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)))`); + + assert.strictEqual(sexprs(semanticObj), expected); + }); + }); +});