From f450bcf5f654cadc59670248d0d2c0b64d7c2fa8 Mon Sep 17 00:00:00 2001 From: merceyz Date: Mon, 30 Mar 2020 21:22:01 +0200 Subject: [PATCH 1/5] fix: handle dom elements in different window frames --- src/generator.ts | 46 ++++++++- src/parser.ts | 6 +- src/types/index.ts | 1 + src/types/props/DOMElement.ts | 24 +++++ test/generator/html-elements/input.d.ts | 6 ++ test/generator/html-elements/output.js | 115 +++++++++++++++++++++++ test/generator/html-elements/output.json | 43 +++++++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 src/types/props/DOMElement.ts create mode 100644 test/generator/html-elements/input.d.ts create mode 100644 test/generator/html-elements/output.js create mode 100644 test/generator/html-elements/output.json diff --git a/src/generator.ts b/src/generator.ts index 3e970cb..2b0cb6d 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -112,6 +112,12 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio } } + if (t.isDOMElementNode(propType)) { + propType.optional = isOptional; + // Handled internally in the validate function + isOptional = true; + } + return `${jsDoc(node)}"${node.name}": ${generate(propType, options)}${ isOptional ? '' : '.isRequired' },`; @@ -160,6 +166,36 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio return `${importedName}.instanceOf(${node.instance})`; } + if (t.isDOMElementNode(node)) { + return `function (props, propName) { + var node = props[propName]; + + if (node == null) { + return ${ + node.optional + ? 'null' + : `new Error("Prop '" + propName + "' is required but wasn't specified")` + } + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.${node.elementType} || node instanceof ${node.elementType}) { + return null + } + + return new Error("The element provided to '" + propName + "' is not an instance of '${ + node.elementType + }'"); + }`; + } + if (t.isArrayNode(node)) { if (t.isAnyNode(node.arrayType)) { return `${importedName}.array`; @@ -171,7 +207,13 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio if (t.isUnionNode(node)) { let [literals, rest] = _.partition(node.types, t.isLiteralNode); literals = _.uniqBy(literals, (x) => x.value); - rest = _.uniqBy(rest, (x) => (t.isInstanceOfNode(x) ? `${x.type}.${x.instance}` : x.type)); + rest = _.uniqBy(rest, (x) => + t.isInstanceOfNode(x) + ? `${x.type}.${x.instance}` + : t.isDOMElementNode(x) + ? `${x.type}.${x.elementType}` + : x.type + ); literals = literals.sort((a, b) => a.value.localeCompare(b.value)); @@ -182,6 +224,8 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio // An interface is PropTypes.shape // Use `ShapeNode` to get it sorted in the correct order return `ShapeNode`; + } else if (t.isDOMElementNode(obj)) { + return `${obj.type}.${obj.elementType}`; } return obj.type; diff --git a/src/parser.ts b/src/parser.ts index 3706395..c79a9e2 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -360,8 +360,10 @@ export function parseFromProgram( return t.instanceOfNode(typeName); } case 'Element': { - // Nextjs: Element isn't defined on the server - return t.instanceOfNode("typeof Element === 'undefined' ? Object : Element"); + return t.DOMElementNode('Element'); + } + case 'HTMLElement': { + return t.DOMElementNode('HTMLElement'); } } } diff --git a/src/types/index.ts b/src/types/index.ts index fffdd10..d382171 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -16,3 +16,4 @@ export * from './props/object'; export * from './props/array'; export * from './props/element'; export * from './props/instanceOf'; +export * from './props/DOMElement'; diff --git a/src/types/props/DOMElement.ts b/src/types/props/DOMElement.ts new file mode 100644 index 0000000..2c439e6 --- /dev/null +++ b/src/types/props/DOMElement.ts @@ -0,0 +1,24 @@ +import { Node } from '../nodes/baseNodes'; + +const typeString = 'DOMElementNode'; +type ElementType = 'Element' | 'HTMLElement'; + +interface DOMElementNode extends Node { + elementType: ElementType; + optional: boolean; +} + +export function DOMElementNode( + elementType: ElementType, + optional: boolean = false +): DOMElementNode { + return { + type: typeString, + elementType, + optional, + }; +} + +export function isDOMElementNode(node: Node): node is DOMElementNode { + return node.type === typeString; +} diff --git a/test/generator/html-elements/input.d.ts b/test/generator/html-elements/input.d.ts new file mode 100644 index 0000000..bead446 --- /dev/null +++ b/test/generator/html-elements/input.d.ts @@ -0,0 +1,6 @@ +export function Foo(props: { + element: Element; + optional?: Element; + htmlElement: HTMLElement; + bothTypes: Element | HTMLElement; +}): JSX.Element; diff --git a/test/generator/html-elements/output.js b/test/generator/html-elements/output.js new file mode 100644 index 0000000..36a8bb6 --- /dev/null +++ b/test/generator/html-elements/output.js @@ -0,0 +1,115 @@ +Foo.propTypes = { + bothTypes: PropTypes.oneOfType([ + function (props, propName) { + var node = props[propName]; + + if (node == null) { + return new Error("Prop '" + propName + "' is required but wasn't specified"); + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.Element || node instanceof Element) { + return null; + } + + return new Error( + "The element provided to '" + propName + "' is not an instance of 'Element'" + ); + }, + function (props, propName) { + var node = props[propName]; + + if (node == null) { + return new Error("Prop '" + propName + "' is required but wasn't specified"); + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.HTMLElement || node instanceof HTMLElement) { + return null; + } + + return new Error( + "The element provided to '" + propName + "' is not an instance of 'HTMLElement'" + ); + }, + ]).isRequired, + element: function (props, propName) { + var node = props[propName]; + + if (node == null) { + return new Error("Prop '" + propName + "' is required but wasn't specified"); + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.Element || node instanceof Element) { + return null; + } + + return new Error("The element provided to '" + propName + "' is not an instance of 'Element'"); + }, + htmlElement: function (props, propName) { + var node = props[propName]; + + if (node == null) { + return new Error("Prop '" + propName + "' is required but wasn't specified"); + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.HTMLElement || node instanceof HTMLElement) { + return null; + } + + return new Error( + "The element provided to '" + propName + "' is not an instance of 'HTMLElement'" + ); + }, + optional: function (props, propName) { + var node = props[propName]; + + if (node == null) { + return null; + } + + var ownerWindow = null; + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + ownerWindow = ownerDocument ? ownerDocument.defaultView : window; + } else { + ownerWindow = node; + } + + if (node instanceof ownerWindow.Element || node instanceof Element) { + return null; + } + + return new Error("The element provided to '" + propName + "' is not an instance of 'Element'"); + }, +}; diff --git a/test/generator/html-elements/output.json b/test/generator/html-elements/output.json new file mode 100644 index 0000000..9cd53be --- /dev/null +++ b/test/generator/html-elements/output.json @@ -0,0 +1,43 @@ +{ + "type": "ProgramNode", + "body": [ + { + "type": "ComponentNode", + "name": "Foo", + "types": [ + { + "type": "PropTypeNode", + "name": "element", + "propType": { "type": "DOMElementNode", "elementType": "Element", "optional": false } + }, + { + "type": "PropTypeNode", + "name": "optional", + "propType": { + "type": "UnionNode", + "types": [ + { "type": "UndefinedNode" }, + { "type": "DOMElementNode", "elementType": "Element", "optional": false } + ] + } + }, + { + "type": "PropTypeNode", + "name": "htmlElement", + "propType": { "type": "DOMElementNode", "elementType": "HTMLElement", "optional": false } + }, + { + "type": "PropTypeNode", + "name": "bothTypes", + "propType": { + "type": "UnionNode", + "types": [ + { "type": "DOMElementNode", "elementType": "Element", "optional": false }, + { "type": "DOMElementNode", "elementType": "HTMLElement", "optional": false } + ] + } + } + ] + } + ] +} From e0afdf5fe5cc87f7188e4ff1731771cdcd45bbdc Mon Sep 17 00:00:00 2001 From: merceyz Date: Fri, 3 Apr 2020 17:17:39 +0200 Subject: [PATCH 2/5] refactor: check nodeType --- src/generator.ts | 32 +------ src/parser.ts | 6 +- src/types/props/DOMElement.ts | 8 +- test/generator/html-elements/output.js | 111 ++++------------------- test/generator/html-elements/output.json | 13 +-- 5 files changed, 30 insertions(+), 140 deletions(-) diff --git a/src/generator.ts b/src/generator.ts index 2b0cb6d..28b7c9a 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -168,9 +168,7 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio if (t.isDOMElementNode(node)) { return `function (props, propName) { - var node = props[propName]; - - if (node == null) { + if (props[propName] == null) { return ${ node.optional ? 'null' @@ -178,21 +176,9 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio } } - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; - } - - if (node instanceof ownerWindow.${node.elementType} || node instanceof ${node.elementType}) { - return null - } - - return new Error("The element provided to '" + propName + "' is not an instance of '${ - node.elementType - }'"); + if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + return new Error("Expected prop '" + propName + "' to be of type Element") + } }`; } @@ -207,13 +193,7 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio if (t.isUnionNode(node)) { let [literals, rest] = _.partition(node.types, t.isLiteralNode); literals = _.uniqBy(literals, (x) => x.value); - rest = _.uniqBy(rest, (x) => - t.isInstanceOfNode(x) - ? `${x.type}.${x.instance}` - : t.isDOMElementNode(x) - ? `${x.type}.${x.elementType}` - : x.type - ); + rest = _.uniqBy(rest, (x) => (t.isInstanceOfNode(x) ? `${x.type}.${x.instance}` : x.type)); literals = literals.sort((a, b) => a.value.localeCompare(b.value)); @@ -224,8 +204,6 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio // An interface is PropTypes.shape // Use `ShapeNode` to get it sorted in the correct order return `ShapeNode`; - } else if (t.isDOMElementNode(obj)) { - return `${obj.type}.${obj.elementType}`; } return obj.type; diff --git a/src/parser.ts b/src/parser.ts index c79a9e2..e92b1a8 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -359,11 +359,9 @@ export function parseFromProgram( case 'React.Component': { return t.instanceOfNode(typeName); } - case 'Element': { - return t.DOMElementNode('Element'); - } + case 'Element': case 'HTMLElement': { - return t.DOMElementNode('HTMLElement'); + return t.DOMElementNode(); } } } diff --git a/src/types/props/DOMElement.ts b/src/types/props/DOMElement.ts index 2c439e6..1dc7986 100644 --- a/src/types/props/DOMElement.ts +++ b/src/types/props/DOMElement.ts @@ -1,20 +1,14 @@ import { Node } from '../nodes/baseNodes'; const typeString = 'DOMElementNode'; -type ElementType = 'Element' | 'HTMLElement'; interface DOMElementNode extends Node { - elementType: ElementType; optional: boolean; } -export function DOMElementNode( - elementType: ElementType, - optional: boolean = false -): DOMElementNode { +export function DOMElementNode(optional: boolean = false): DOMElementNode { return { type: typeString, - elementType, optional, }; } diff --git a/test/generator/html-elements/output.js b/test/generator/html-elements/output.js index 36a8bb6..455bcf6 100644 --- a/test/generator/html-elements/output.js +++ b/test/generator/html-elements/output.js @@ -1,115 +1,38 @@ Foo.propTypes = { - bothTypes: PropTypes.oneOfType([ - function (props, propName) { - var node = props[propName]; - - if (node == null) { - return new Error("Prop '" + propName + "' is required but wasn't specified"); - } - - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; - } - - if (node instanceof ownerWindow.Element || node instanceof Element) { - return null; - } - - return new Error( - "The element provided to '" + propName + "' is not an instance of 'Element'" - ); - }, - function (props, propName) { - var node = props[propName]; - - if (node == null) { - return new Error("Prop '" + propName + "' is required but wasn't specified"); - } - - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; - } - - if (node instanceof ownerWindow.HTMLElement || node instanceof HTMLElement) { - return null; - } - - return new Error( - "The element provided to '" + propName + "' is not an instance of 'HTMLElement'" - ); - }, - ]).isRequired, - element: function (props, propName) { - var node = props[propName]; - - if (node == null) { + bothTypes: function (props, propName) { + if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); } - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; + if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + return new Error("Expected prop '" + propName + "' to be of type Element"); } - - if (node instanceof ownerWindow.Element || node instanceof Element) { - return null; + }.isRequired, + element: function (props, propName) { + if (props[propName] == null) { + return new Error("Prop '" + propName + "' is required but wasn't specified"); } - return new Error("The element provided to '" + propName + "' is not an instance of 'Element'"); + if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + return new Error("Expected prop '" + propName + "' to be of type Element"); + } }, htmlElement: function (props, propName) { - var node = props[propName]; - - if (node == null) { + if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); } - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; - } - - if (node instanceof ownerWindow.HTMLElement || node instanceof HTMLElement) { - return null; + if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + return new Error("Expected prop '" + propName + "' to be of type Element"); } - - return new Error( - "The element provided to '" + propName + "' is not an instance of 'HTMLElement'" - ); }, optional: function (props, propName) { - var node = props[propName]; - - if (node == null) { + if (props[propName] == null) { return null; } - var ownerWindow = null; - if (node.toString() !== '[object Window]') { - var ownerDocument = node.ownerDocument; - ownerWindow = ownerDocument ? ownerDocument.defaultView : window; - } else { - ownerWindow = node; - } - - if (node instanceof ownerWindow.Element || node instanceof Element) { - return null; + if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + return new Error("Expected prop '" + propName + "' to be of type Element"); } - - return new Error("The element provided to '" + propName + "' is not an instance of 'Element'"); }, }; diff --git a/test/generator/html-elements/output.json b/test/generator/html-elements/output.json index 9cd53be..1c83133 100644 --- a/test/generator/html-elements/output.json +++ b/test/generator/html-elements/output.json @@ -8,23 +8,20 @@ { "type": "PropTypeNode", "name": "element", - "propType": { "type": "DOMElementNode", "elementType": "Element", "optional": false } + "propType": { "type": "DOMElementNode", "optional": false } }, { "type": "PropTypeNode", "name": "optional", "propType": { "type": "UnionNode", - "types": [ - { "type": "UndefinedNode" }, - { "type": "DOMElementNode", "elementType": "Element", "optional": false } - ] + "types": [{ "type": "UndefinedNode" }, { "type": "DOMElementNode", "optional": false }] } }, { "type": "PropTypeNode", "name": "htmlElement", - "propType": { "type": "DOMElementNode", "elementType": "HTMLElement", "optional": false } + "propType": { "type": "DOMElementNode", "optional": false } }, { "type": "PropTypeNode", @@ -32,8 +29,8 @@ "propType": { "type": "UnionNode", "types": [ - { "type": "DOMElementNode", "elementType": "Element", "optional": false }, - { "type": "DOMElementNode", "elementType": "HTMLElement", "optional": false } + { "type": "DOMElementNode", "optional": false }, + { "type": "DOMElementNode", "optional": false } ] } } From 2564c8c60c89364e7681291a0c807499516a6c46 Mon Sep 17 00:00:00 2001 From: merceyz Date: Fri, 3 Apr 2020 17:35:08 +0200 Subject: [PATCH 3/5] fix: uniq union --- src/generator.ts | 5 ++--- src/parser.ts | 4 +++- src/types/props/DOMElement.ts | 4 ++-- src/types/props/union.ts | 23 +++++++++++++++++++-- test/generator/html-elements/output.js | 2 +- test/generator/html-elements/output.json | 26 ++++-------------------- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/generator.ts b/src/generator.ts index 28b7c9a..ea288c4 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -1,5 +1,6 @@ import * as t from './types'; import _ from 'lodash'; +import { uniqUnion } from './types'; export interface GenerateOptions { /** @@ -191,9 +192,7 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio } if (t.isUnionNode(node)) { - let [literals, rest] = _.partition(node.types, t.isLiteralNode); - literals = _.uniqBy(literals, (x) => x.value); - rest = _.uniqBy(rest, (x) => (t.isInstanceOfNode(x) ? `${x.type}.${x.instance}` : x.type)); + let [literals, rest] = _.partition(uniqUnion(node).types, t.isLiteralNode); literals = literals.sort((a, b) => a.value.localeCompare(b.value)); diff --git a/src/parser.ts b/src/parser.ts index e92b1a8..afe22c2 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -374,7 +374,9 @@ export function parseFromProgram( } if (type.isUnion()) { - return t.unionNode(type.types.map((x) => checkType(x, typeStack, name))); + const node = t.unionNode(type.types.map((x) => checkType(x, typeStack, name))); + + return node.types.length === 1 ? node.types[0] : node; } if (type.flags & ts.TypeFlags.String) { diff --git a/src/types/props/DOMElement.ts b/src/types/props/DOMElement.ts index 1dc7986..aa32a6d 100644 --- a/src/types/props/DOMElement.ts +++ b/src/types/props/DOMElement.ts @@ -3,10 +3,10 @@ import { Node } from '../nodes/baseNodes'; const typeString = 'DOMElementNode'; interface DOMElementNode extends Node { - optional: boolean; + optional?: boolean; } -export function DOMElementNode(optional: boolean = false): DOMElementNode { +export function DOMElementNode(optional?: boolean): DOMElementNode { return { type: typeString, optional, diff --git a/src/types/props/union.ts b/src/types/props/union.ts index 9151299..e03a986 100644 --- a/src/types/props/union.ts +++ b/src/types/props/union.ts @@ -1,3 +1,5 @@ +import _ from 'lodash'; +import * as t from '../../types'; import { Node } from '../nodes/baseNodes'; const typeString = 'UnionNode'; @@ -21,12 +23,29 @@ export function unionNode(types: Node[]): UnionNode { }); } - return { + return uniqUnion({ type: typeString, types: flatTypes, - }; + }); } export function isUnionNode(node: Node): node is UnionNode { return node.type === typeString; } + +export function uniqUnion(node: UnionNode): UnionNode { + return { + type: node.type, + types: _.uniqBy(node.types, (x) => { + if (t.isLiteralNode(x)) { + return x.value; + } + + if (t.isInstanceOfNode(x)) { + return `${x.type}.${x.instance}`; + } + + return x.type; + }), + }; +} diff --git a/test/generator/html-elements/output.js b/test/generator/html-elements/output.js index 455bcf6..03a2fe4 100644 --- a/test/generator/html-elements/output.js +++ b/test/generator/html-elements/output.js @@ -7,7 +7,7 @@ Foo.propTypes = { if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element"); } - }.isRequired, + }, element: function (props, propName) { if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); diff --git a/test/generator/html-elements/output.json b/test/generator/html-elements/output.json index 1c83133..8f7ff71 100644 --- a/test/generator/html-elements/output.json +++ b/test/generator/html-elements/output.json @@ -5,35 +5,17 @@ "type": "ComponentNode", "name": "Foo", "types": [ - { - "type": "PropTypeNode", - "name": "element", - "propType": { "type": "DOMElementNode", "optional": false } - }, + { "type": "PropTypeNode", "name": "element", "propType": { "type": "DOMElementNode" } }, { "type": "PropTypeNode", "name": "optional", "propType": { "type": "UnionNode", - "types": [{ "type": "UndefinedNode" }, { "type": "DOMElementNode", "optional": false }] + "types": [{ "type": "UndefinedNode" }, { "type": "DOMElementNode" }] } }, - { - "type": "PropTypeNode", - "name": "htmlElement", - "propType": { "type": "DOMElementNode", "optional": false } - }, - { - "type": "PropTypeNode", - "name": "bothTypes", - "propType": { - "type": "UnionNode", - "types": [ - { "type": "DOMElementNode", "optional": false }, - { "type": "DOMElementNode", "optional": false } - ] - } - } + { "type": "PropTypeNode", "name": "htmlElement", "propType": { "type": "DOMElementNode" } }, + { "type": "PropTypeNode", "name": "bothTypes", "propType": { "type": "DOMElementNode" } } ] } ] From 4a940f5e28ecda6e29e826d8a1851e275ce11128 Mon Sep 17 00:00:00 2001 From: merceyz Date: Fri, 3 Apr 2020 17:42:27 +0200 Subject: [PATCH 4/5] refactor: rename function --- src/generator.ts | 3 +-- src/types/props/union.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/generator.ts b/src/generator.ts index ea288c4..478bb27 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -1,6 +1,5 @@ import * as t from './types'; import _ from 'lodash'; -import { uniqUnion } from './types'; export interface GenerateOptions { /** @@ -192,7 +191,7 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio } if (t.isUnionNode(node)) { - let [literals, rest] = _.partition(uniqUnion(node).types, t.isLiteralNode); + let [literals, rest] = _.partition(t.uniqueUnionTypes(node).types, t.isLiteralNode); literals = literals.sort((a, b) => a.value.localeCompare(b.value)); diff --git a/src/types/props/union.ts b/src/types/props/union.ts index e03a986..f39cf2a 100644 --- a/src/types/props/union.ts +++ b/src/types/props/union.ts @@ -23,7 +23,7 @@ export function unionNode(types: Node[]): UnionNode { }); } - return uniqUnion({ + return uniqueUnionTypes({ type: typeString, types: flatTypes, }); @@ -33,7 +33,7 @@ export function isUnionNode(node: Node): node is UnionNode { return node.type === typeString; } -export function uniqUnion(node: UnionNode): UnionNode { +export function uniqueUnionTypes(node: UnionNode): UnionNode { return { type: node.type, types: _.uniqBy(node.types, (x) => { From 7ad95e25ea698288d775680f7aa9f5789595dca7 Mon Sep 17 00:00:00 2001 From: merceyz Date: Fri, 3 Apr 2020 17:45:05 +0200 Subject: [PATCH 5/5] refactor(generator): make the output smaller --- src/generator.ts | 4 +--- test/generator/html-elements/output.js | 16 ++++------------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/generator.ts b/src/generator.ts index 478bb27..1d42f2e 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -174,9 +174,7 @@ export function generate(node: t.Node | t.PropTypeNode[], options: GenerateOptio ? 'null' : `new Error("Prop '" + propName + "' is required but wasn't specified")` } - } - - if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element") } }`; diff --git a/test/generator/html-elements/output.js b/test/generator/html-elements/output.js index 03a2fe4..2f83837 100644 --- a/test/generator/html-elements/output.js +++ b/test/generator/html-elements/output.js @@ -2,36 +2,28 @@ Foo.propTypes = { bothTypes: function (props, propName) { if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); - } - - if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element"); } }, element: function (props, propName) { if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); - } - - if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element"); } }, htmlElement: function (props, propName) { if (props[propName] == null) { return new Error("Prop '" + propName + "' is required but wasn't specified"); - } - - if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element"); } }, optional: function (props, propName) { if (props[propName] == null) { return null; - } - - if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { + } else if (typeof props[propName] !== 'object' || props[propName].nodeType !== 1) { return new Error("Expected prop '" + propName + "' to be of type Element"); } },