diff --git a/@plotly/dash-generator-test-component-typescript/generator.test.ts b/@plotly/dash-generator-test-component-typescript/generator.test.ts index a36fd21452..c2920b136d 100644 --- a/@plotly/dash-generator-test-component-typescript/generator.test.ts +++ b/@plotly/dash-generator-test-component-typescript/generator.test.ts @@ -215,6 +215,21 @@ describe('Test Typescript component metadata generation', () => { }); } ); + + test( + 'Nested props to any', () => { + expect( + R.path([ + 'TypeScriptComponent', + 'props', + 'nested', + 'type', + 'value', + 'nested', + 'name' + ], metadata)).toBe('any') + } + ) }); describe('Test component comments', () => { diff --git a/@plotly/dash-generator-test-component-typescript/src/props.ts b/@plotly/dash-generator-test-component-typescript/src/props.ts index 5c7e72a7be..86d84af5f6 100644 --- a/@plotly/dash-generator-test-component-typescript/src/props.ts +++ b/@plotly/dash-generator-test-component-typescript/src/props.ts @@ -1,6 +1,11 @@ // Needs to export types if not in a d.ts file or if any import is present in the d.ts import React from 'react'; + +type Nested = { + nested: Nested; +} + export type TypescriptComponentProps = { children?: React.ReactNode; id?: string; @@ -35,6 +40,8 @@ export type TypescriptComponentProps = { setProps?: (props: Record) => void; className?: string; style?: any; + + nested?: Nested; }; export type WrappedHTMLProps = { diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3f8d9c3a..aec19f1a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). `dangerously_allow_html=True` + `mathjax=True` works in some cases, and in some cases not. - [#2047](https://github.com/plotly/dash/pull/2047) Fix bug [#1979](https://github.com/plotly/dash/issues/1979) in which `DASH_DEBUG` as enviroment variable gets ignored. - [#2065](https://github.com/plotly/dash/pull/2065) Fix bug [#2064](https://github.com/plotly/dash/issues/2064) rendering of `dcc.Dropdown` with a value but no options. +- [#2070](https://github.com/plotly/dash/pull/2070) Fix bug [#2066](https://github.com/plotly/dash/issues/2066) nested types triggering maximum call stack error when building typescript components. ### Changed diff --git a/dash/extract-meta.js b/dash/extract-meta.js index bb74d68266..812738725c 100755 --- a/dash/extract-meta.js +++ b/dash/extract-meta.js @@ -55,6 +55,13 @@ const PRIMITIVES = [ 'node' ]; +// These types take too long to parse because of heavy nesting. +const BANNED_TYPES = [ + 'Document', + 'ShadowRoot', + 'ChildNode', + 'ParentNode', +]; const unionSupport = PRIMITIVES.concat('boolean', 'Element'); const reArray = new RegExp(`(${unionSupport.join('|')})\\[\\]`); @@ -236,9 +243,10 @@ function gatherComponents(sources, components = {}) { })) }); - const getUnion = (typeObj, propObj) => { + const getUnion = (typeObj, propObj, parentType) => { let name = 'union', value; + // Union only do base types value = typeObj.types .filter(t => { @@ -253,7 +261,7 @@ function gatherComponents(sources, components = {}) { isArray(checker.typeToString(t)) ); }) - .map(t => getPropType(t, propObj)); + .map(t => getPropType(t, propObj, parentType)); if (!value.length) { name = 'any'; @@ -282,17 +290,19 @@ function gatherComponents(sources, components = {}) { return propName; }; - const getPropType = (propType, propObj) => { + const getPropType = (propType, propObj, parentType = null) => { // Types can get namespace prefixes or not. let name = checker.typeToString(propType).replace(/^React\./, ''); let value; const raw = name; + const newParentType = (parentType || []).concat(raw) + if (propType.isUnion()) { if (isUnionLiteral(propType)) { return {...getEnum(propType), raw}; } else if (raw.includes('|')) { - return {...getUnion(propType, propObj), raw}; + return {...getUnion(propType, propObj, newParentType), raw}; } } @@ -318,12 +328,20 @@ function gatherComponents(sources, components = {}) { const [nodeType] = checker.getTypeArguments(propType); if (nodeType) { - value = getPropType(nodeType, propObj); + value = getPropType( + nodeType, propObj, newParentType, + ); } else { // Not sure, might be unsupported here. name = 'array'; } } + } else if ( + BANNED_TYPES.includes(name) || + (parentType && parentType.includes(name)) + ) { + console.error(`Warning nested type: ${name}`); + name = 'any'; } else { name = 'shape'; // If the type is declared as union it will have a types attribute. @@ -331,7 +349,10 @@ function gatherComponents(sources, components = {}) { if (isUnionLiteral(propType)) { return {...getEnum(propType), raw}; } - return {...getUnion(propType, propObj), raw}; + return { + ...getUnion(propType, propObj, newParentType), + raw + }; } value = getProps( @@ -339,7 +360,8 @@ function gatherComponents(sources, components = {}) { propObj, [], {}, - true + true, + newParentType, ); } } @@ -543,7 +565,8 @@ function gatherComponents(sources, components = {}) { propsObj, baseProps = [], defaultProps = {}, - flat = false + flat = false, + parentType = null, ) => { const results = {}; @@ -571,7 +594,7 @@ function gatherComponents(sources, components = {}) { required, defaultValue }; - const type = getPropType(propType, propsObj); + const type = getPropType(propType, propsObj, parentType); // root object is inserted as type, // otherwise it's flat in the value prop. if (!flat) {