Skip to content

Commit

Permalink
refactor(ns-workflows-1): change from stamps to TypeScript classes (#…
Browse files Browse the repository at this point in the history
…3720)

Co-authored-by: Vladimír Gorej <[email protected]>

Refs #3481
  • Loading branch information
glowcloud authored Jan 24, 2024
1 parent dd369a0 commit efb18a7
Show file tree
Hide file tree
Showing 45 changed files with 811 additions and 711 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

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

Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ const SpecificationVisitor = stampit(Visitor, {
retrieveVisitorInstance(specPath, options = {}) {
const passingOpts = this.retrievePassingOptions();

return this.retrieveVisitor(specPath)({ ...passingOpts, ...options });
const VisitorClz = this.retrieveVisitor(specPath) as typeof Visitor;
const visitorOpts = { ...passingOpts, ...options };

// @ts-ignore
return new VisitorClz(visitorOpts as any);
},

toRefractedElement(specPath: string[], element, options = {}) {
Expand Down
2 changes: 1 addition & 1 deletion packages/apidom-ns-workflows-1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"@types/ramda": "~0.29.6",
"ramda": "~0.29.1",
"ramda-adjunct": "^4.1.1",
"stampit": "^4.3.2"
"ts-mixer": "^6.0.3"
},
"files": [
"cjs/",
Expand Down
7 changes: 0 additions & 7 deletions packages/apidom-ns-workflows-1/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,6 @@ export {

export { isWorkflowsSpecificationExtension } from './refractor/predicates';

export { default as FixedFieldsVisitor } from './refractor/visitors/generics/FixedFieldsVisitor';
export { default as MapVisitor } from './refractor/visitors/generics/MapVisitor';
export { default as MixedFieldsVisitor } from './refractor/visitors/generics/MixedFieldsVisitor';
export { default as FallbackVisitor } from './refractor/visitors/FallbackVisitor';
export { default as SpecificationVisitor } from './refractor/visitors/SpecificationVisitor';
export { default as Visitor } from './refractor/visitors/Visitor';

export { keyMap, getNodeType } from './traversal/visitor';

// Workflows 1.0.0 elements
Expand Down
12 changes: 7 additions & 5 deletions packages/apidom-ns-workflows-1/src/refractor/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { invokeArgs } from 'ramda-adjunct';
import {
visit,
Element,
dereference,
refract as baseRefract,
dispatchRefractorPlugins,
} from '@swagger-api/apidom-core';
import { path } from 'ramda';

import type VisitorClass from './visitors/Visitor';
import specification from './specification';
import { keyMap, getNodeType } from '../traversal/visitor';
import createToolbox from './toolbox';
Expand All @@ -26,17 +27,18 @@ const refract = <T extends Element>(
* We don't allow consumers to hook into this translation.
* Though we allow consumers to define their onw plugins on already transformed ApiDOM.
*/
const rootVisitor = invokeArgs(specPath, [], resolvedSpec);
// @ts-ignore
visit(element, rootVisitor, { state: { specObj: resolvedSpec } });
const RootVisitorClass = path(specPath, resolvedSpec) as typeof VisitorClass;
const rootVisitor = new RootVisitorClass({ specObj: resolvedSpec });

visit(element, rootVisitor);

/**
* Running plugins visitors means extra single traversal === performance hit.
*/
return dispatchRefractorPlugins(rootVisitor.element, plugins, {
toolboxCreator: createToolbox,
visitorOptions: { keyMap, nodeTypeGetter: getNodeType },
});
}) as T;
};

export const createRefractor =
Expand Down
2 changes: 0 additions & 2 deletions packages/apidom-ns-workflows-1/src/refractor/predicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ export interface ReferenceLikeElement extends ObjectElement {
hasKey: (value: '$ref') => true;
}

// eslint-disable-next-line import/prefer-default-export
export const isWorkflowsSpecificationExtension = (element: MemberElement): boolean => {
// @ts-ignore
return isStringElement(element.key) && startsWith('x-', toValue(element.key));
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import stampit from 'stampit';
import { Element, BREAK, cloneDeep } from '@swagger-api/apidom-core';

import Visitor from './Visitor';
import Visitor, { VisitorOptions } from './Visitor';

/**
* This visitor is responsible for falling back to current traversed element.
* Given WorkflowsSpecificationVisitor expects ObjectElement to be traversed. If
* different Element is provided FallBackVisitor is responsible to assigning
* this Element as current element.
*/
const FallbackVisitor = stampit(Visitor, {
methods: {
enter(element: Element) {
this.element = cloneDeep(element);
return BREAK;
},
},
});

export type { VisitorOptions as FallbackVisitorOptions };

class FallbackVisitor extends Visitor {
enter(element: Element) {
this.element = cloneDeep(element);
return BREAK;
}
}

export default FallbackVisitor;
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import stampit from 'stampit';
import { MemberElement, BREAK, cloneDeep } from '@swagger-api/apidom-core';

import SpecificationVisitor from './SpecificationVisitor';

const SpecificationExtensionVisitor = stampit(SpecificationVisitor, {
methods: {
MemberElement(memberElement: MemberElement) {
this.element = cloneDeep(memberElement);
this.element.classes.push('specification-extension');
class SpecificationExtensionVisitor extends SpecificationVisitor {
public declare element: MemberElement;

return BREAK;
},
},
});
MemberElement(memberElement: MemberElement) {
this.element = cloneDeep(memberElement);
this.element.classes.push('specification-extension');

return BREAK;
}
}

export default SpecificationExtensionVisitor;
Original file line number Diff line number Diff line change
@@ -1,77 +1,77 @@
import stampit from 'stampit';
import { pathSatisfies, path, pick } from 'ramda';
import { isFunction, isUndefined } from 'ramda-adjunct';
import { isFunction } from 'ramda-adjunct';
import { visit, cloneDeep } from '@swagger-api/apidom-core';

import { keyMap, getNodeType } from '../../traversal/visitor';
import Visitor from './Visitor';
import Visitor, { VisitorOptions } from './Visitor';
import FallbackVisitor from './FallbackVisitor';
import type specification from '../specification';

/**
* This is a base Type for every visitor that does
* internal look-ups to retrieve other child visitors.
*/
const SpecificationVisitor = stampit(Visitor, {
props: {
specObj: null,
passingOptionsNames: ['specObj'],
},
// @ts-ignore
init({ specObj = this.specObj }) {
export interface SpecificationVisitorOptions extends VisitorOptions {
readonly specObj: typeof specification;
}

class SpecificationVisitor extends Visitor {
protected readonly specObj!: typeof specification;

protected readonly passingOptionsNames = ['specObj'];

constructor({ specObj, ...rest }: SpecificationVisitorOptions) {
super({ ...rest });
this.specObj = specObj;
},
methods: {
retrievePassingOptions() {
return pick(this.passingOptionsNames, this);
},
}

retrievePassingOptions() {
return pick(this.passingOptionsNames as (keyof this)[], this);
}

retrieveFixedFields(specPath) {
const fixedFields = path(['visitors', ...specPath, 'fixedFields'], this.specObj);
if (typeof fixedFields === 'object' && fixedFields !== null) {
return Object.keys(fixedFields);
}
return [];
},
retrieveFixedFields(specPath: string[]) {
const fixedFields = path(['visitors', ...specPath, 'fixedFields'], this.specObj);
if (typeof fixedFields === 'object' && fixedFields !== null) {
return Object.keys(fixedFields);
}
return [];
}

retrieveVisitor(specPath) {
if (pathSatisfies(isFunction, ['visitors', ...specPath], this.specObj)) {
return path(['visitors', ...specPath], this.specObj);
}
retrieveVisitor(specPath: string[]) {
if (pathSatisfies(isFunction, ['visitors', ...specPath], this.specObj)) {
return path(['visitors', ...specPath], this.specObj);
}

return path(['visitors', ...specPath, '$visitor'], this.specObj);
},
return path(['visitors', ...specPath, '$visitor'], this.specObj);
}

retrieveVisitorInstance(specPath, options = {}) {
const passingOpts = this.retrievePassingOptions();
retrieveVisitorInstance(specPath: string[], options = {}): Visitor {
const passingOpts = this.retrievePassingOptions();
const VisitorClz = this.retrieveVisitor(specPath) as typeof Visitor;
const visitorOpts = { ...passingOpts, ...options };

return this.retrieveVisitor(specPath)({ ...passingOpts, ...options });
},
return new VisitorClz(visitorOpts);
}

toRefractedElement(specPath: string[], element, options = {}) {
/**
* This is `Visitor shortcut`: mechanism for short circuiting the traversal and replacing
* it by basic node cloning.
*
* Visiting the element is equivalent to cloning it if the prototype of a visitor
* is the same as the prototype of FallbackVisitor. If that's the case, we can avoid
* bootstrapping the traversal cycle for fields that don't require any special visiting.
*/
const visitor = this.retrieveVisitorInstance(specPath, options);
const visitorPrototype = Object.getPrototypeOf(visitor);
toRefractedElement(specPath: string[], element: any, options = {}) {
/**
* This is `Visitor shortcut`: mechanism for short-circuiting the traversal and replacing
* it by basic node cloning.
*
* Visiting the element is equivalent to cloning it if the prototype of a visitor
* is the same as the prototype of FallbackVisitor. If that's the case, we can avoid
* bootstrapping the traversal cycle for fields that don't require any special visiting.
*/
const visitor = this.retrieveVisitorInstance(specPath, options);

if (isUndefined(this.fallbackVisitorPrototype)) {
this.fallbackVisitorPrototype = Object.getPrototypeOf(
this.retrieveVisitorInstance(['value']),
);
}
if (this.fallbackVisitorPrototype === visitorPrototype) {
return cloneDeep(element);
}
if (visitor instanceof FallbackVisitor && visitor?.constructor === FallbackVisitor) {
return cloneDeep(element);
}

// standard processing continues
visit(element, visitor, { keyMap, ...options, nodeTypeGetter: getNodeType });
return visitor.element;
},
},
});
// @ts-ignore
visit(element, visitor, { keyMap, ...options, nodeTypeGetter: getNodeType });
return visitor.element;
}
}

export default SpecificationVisitor;
33 changes: 18 additions & 15 deletions packages/apidom-ns-workflows-1/src/refractor/visitors/Visitor.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import stampit from 'stampit';
import { hasElementSourceMap } from '@swagger-api/apidom-core';
import { hasElementSourceMap, Element } from '@swagger-api/apidom-core';

const Visitor = stampit({
props: {
element: null,
},
methods: {
copyMetaAndAttributes(from, to) {
// copy sourcemaps
if (hasElementSourceMap(from)) {
to.meta.set('sourceMap', from.meta.get('sourceMap'));
}
},
},
});
export interface VisitorOptions {}

class Visitor {
public element!: Element;

constructor(options: VisitorOptions = {}) {
Object.assign(this, options);
}

// eslint-disable-next-line class-methods-use-this
public copyMetaAndAttributes(from: Element, to: Element) {
// copy sourcemaps
if (hasElementSourceMap(from)) {
to.meta.set('sourceMap', from.meta.get('sourceMap'));
}
}
}

export default Visitor;
Loading

0 comments on commit efb18a7

Please sign in to comment.