From f99e111d82fe0a66e147b0908a8219f3ca42cc06 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Fri, 10 Jun 2022 08:27:56 +0100 Subject: [PATCH 1/3] feat: add better handing for typescript complier fix: soft file there is no props defined Check if export is either a class or function that returns JSX element This allows for none exported JSX functions in the file. Signed-off-by: Jordan Hall --- dash/extract-meta.js | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/dash/extract-meta.js b/dash/extract-meta.js index 0ed83954bc..c95922c88e 100755 --- a/dash/extract-meta.js +++ b/dash/extract-meta.js @@ -25,7 +25,7 @@ function help() { console.error('usage: '); console.error( 'extract-meta ^fileIgnorePattern ^forbidden$|^props$|^patterns$' + - ' path/to/component(s) [path/to/more/component(s) ...] > metadata.json' + ' path/to/component(s) [path/to/more/component(s) ...] > metadata.json' ); } @@ -98,7 +98,7 @@ function isReservedPropName(propName) { if (reservedPattern.test(propName)) { process.stderr.write( `\nERROR: "${propName}" matches reserved word ` + - `pattern: ${reservedPattern.toString()}\n` + `pattern: ${reservedPattern.toString()}\n` ); failedBuild = true; } @@ -118,9 +118,14 @@ function checkDocstring(name, value) { function docstringWarning(doc) { checkDocstring(doc.displayName, doc.description); - Object.entries(doc.props || {}).forEach(([name, p]) => - checkDocstring(`${doc.displayName}.${name}`, p.description) - ); + if (doc && doc.props) { + Object.entries(doc.props || {}).forEach(([name, p]) => + checkDocstring(`${doc.displayName}.${name}`, p.description) + ); + } else { + // Soft fail + console.warn(`${doc.displayName} does not have any props`) + } } function zipArrays(...arrays) { @@ -160,9 +165,9 @@ function gatherComponents(sources, components = {}) { const extension = path.extname(filepath); if (['.jsx', '.js'].includes(extension)) { components[cleanPath(filepath)] = parseJSX(filepath); - } else if (filepath.endsWith('.tsx')) { + } else if (['.tsx', 'ts'].includes(extension)) { try { - const name = /(.*)\.tsx/.exec(path.basename(filepath))[1]; + const name = /(.*)\.(ts|tsx)/.exec(path.basename(filepath))[1]; filepaths.push(filepath); names.push(name); } catch (err) { @@ -720,6 +725,23 @@ function gatherComponents(sources, components = {}) { } } + const isArrowFunction = typeSymbol.declarations && typeSymbol.declarations[0].kind === ts.SyntaxKind.ArrowFunction + const isClass = ts.isClassDeclaration(declaration); + + if (!isArrowFunction && !isClass) { + // we do not care about these exports + return null + } + + if (isArrowFunction) { + const signature = checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; + const returnType = checker.typeToString(signature.getReturnType()); + if (returnType !== 'Element') { + // Not JSX so no need to classifiy as compnent + return null; + } + } + let defaultProps = getDefaultProps(typeSymbol, source); const propsType = getPropsForFunctionalComponent(type); const isContext = !!type.getProperty('isContext'); @@ -761,12 +783,14 @@ function gatherComponents(sources, components = {}) { .join(''); } const doc = { - displayName: name, + displayName: `${name} - ${rootExp.name}`, description, props, isContext }; docstringWarning(doc); + + // todo: Add support for mutiple components in single file components[cleanPath(filepath)] = doc; }); }); From b3c3e2aa87a9a2ebd9a6bff23bdf14318b9a221e Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Fri, 10 Jun 2022 09:11:29 +0100 Subject: [PATCH 2/3] fix: name has to be as it was otherwise it cant extract ok --- .../package-lock.json | 24 +++++++++++++++++ .../package.json | 1 + dash/extract-meta.js | 26 ++++++++++--------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/@plotly/dash-generator-test-component-typescript/package-lock.json b/@plotly/dash-generator-test-component-typescript/package-lock.json index 28cf26e996..27a09f5d3b 100644 --- a/@plotly/dash-generator-test-component-typescript/package-lock.json +++ b/@plotly/dash-generator-test-component-typescript/package-lock.json @@ -2695,6 +2695,15 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "dev": true }, + "node_modules/@types/ramda": { + "version": "0.28.14", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.28.14.tgz", + "integrity": "sha512-hLgAeKxS5MpIEROaIRHscFqWf+V04CB+A0Kq+OjnTs1fEGHXEs4aeOhXIRovAPe6PfWYKHEwEkVIYWf98OjxnA==", + "dev": true, + "dependencies": { + "ts-toolbelt": "^6.15.1" + } + }, "node_modules/@types/react": { "version": "17.0.47", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.47.tgz", @@ -10603,6 +10612,15 @@ "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "dev": true }, + "@types/ramda": { + "version": "0.28.14", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.28.14.tgz", + "integrity": "sha512-hLgAeKxS5MpIEROaIRHscFqWf+V04CB+A0Kq+OjnTs1fEGHXEs4aeOhXIRovAPe6PfWYKHEwEkVIYWf98OjxnA==", + "dev": true, + "requires": { + "ts-toolbelt": "^6.15.1" + } + }, "@types/react": { "version": "17.0.47", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.47.tgz", @@ -14689,6 +14707,12 @@ } } }, + "ts-toolbelt": { + "version": "6.15.5", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz", + "integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==", + "dev": true + }, "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", diff --git a/@plotly/dash-generator-test-component-typescript/package.json b/@plotly/dash-generator-test-component-typescript/package.json index 4a4695f2d0..39b0857389 100644 --- a/@plotly/dash-generator-test-component-typescript/package.json +++ b/@plotly/dash-generator-test-component-typescript/package.json @@ -20,6 +20,7 @@ "@babel/preset-env": "^7.19.4", "@babel/preset-react": "^7.18.6", "@types/jest": "^29.2.0", + "@types/ramda": "^0.28.14", "@types/react": "^17.0.39", "babel-loader": "^8.2.5", "jest": "^29.2.1", diff --git a/dash/extract-meta.js b/dash/extract-meta.js index c95922c88e..c98133b6c3 100755 --- a/dash/extract-meta.js +++ b/dash/extract-meta.js @@ -118,13 +118,14 @@ function checkDocstring(name, value) { function docstringWarning(doc) { checkDocstring(doc.displayName, doc.description); + const exportedDisplayName = doc.exportedName || doc.displayName if (doc && doc.props) { Object.entries(doc.props || {}).forEach(([name, p]) => - checkDocstring(`${doc.displayName}.${name}`, p.description) + checkDocstring(`${exportedDisplayName}.${name}`, p.description) ); } else { // Soft fail - console.warn(`${doc.displayName} does not have any props`) + console.warn(`${exportedDisplayName} does not have any props`) } } @@ -733,14 +734,14 @@ function gatherComponents(sources, components = {}) { return null } - if (isArrowFunction) { - const signature = checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; - const returnType = checker.typeToString(signature.getReturnType()); - if (returnType !== 'Element') { - // Not JSX so no need to classifiy as compnent - return null; - } - } + // if (isArrowFunction) { + // const signature = checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; + // const returnType = checker.typeToString(signature.getReturnType()); + // if (returnType !== 'Element') { + // // Not JSX so no need to classifiy as compnent + // return null; + // } + // } let defaultProps = getDefaultProps(typeSymbol, source); const propsType = getPropsForFunctionalComponent(type); @@ -783,10 +784,11 @@ function gatherComponents(sources, components = {}) { .join(''); } const doc = { - displayName: `${name} - ${rootExp.name}`, + displayName: name, description, props, - isContext + isContext, + exportedName: rootExp.name }; docstringWarning(doc); From 833974036184b80fbfc7ebc51af720df7cb08370 Mon Sep 17 00:00:00 2001 From: Jordan Hall Date: Fri, 10 Jun 2022 16:15:09 +0100 Subject: [PATCH 3/3] fix: support null and any return type --- .../package-lock.json | 7 +++++++ .../src/components/WrappedHTML.tsx | 2 +- .../src/index.ts | 2 +- dash/extract-meta.js | 21 ++++++++++--------- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/@plotly/dash-generator-test-component-typescript/package-lock.json b/@plotly/dash-generator-test-component-typescript/package-lock.json index 27a09f5d3b..e79cc14db4 100644 --- a/@plotly/dash-generator-test-component-typescript/package-lock.json +++ b/@plotly/dash-generator-test-component-typescript/package-lock.json @@ -12,6 +12,7 @@ "@babel/preset-env": "^7.19.4", "@babel/preset-react": "^7.18.6", "@types/jest": "^29.2.0", + "@types/ramda": "^0.28.14", "@types/react": "^17.0.39", "babel-loader": "^8.2.5", "jest": "^29.2.1", @@ -8182,6 +8183,12 @@ "node": ">=8" } }, + "node_modules/ts-toolbelt": { + "version": "6.15.5", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-6.15.5.tgz", + "integrity": "sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==", + "dev": true + }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", diff --git a/@plotly/dash-generator-test-component-typescript/src/components/WrappedHTML.tsx b/@plotly/dash-generator-test-component-typescript/src/components/WrappedHTML.tsx index df18d78b4e..1044b70bc6 100644 --- a/@plotly/dash-generator-test-component-typescript/src/components/WrappedHTML.tsx +++ b/@plotly/dash-generator-test-component-typescript/src/components/WrappedHTML.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {WrappedHTMLProps} from '../props'; +import { WrappedHTMLProps } from '../props'; /** * Component docstring diff --git a/@plotly/dash-generator-test-component-typescript/src/index.ts b/@plotly/dash-generator-test-component-typescript/src/index.ts index d4dacf00c3..93f185ad6e 100644 --- a/@plotly/dash-generator-test-component-typescript/src/index.ts +++ b/@plotly/dash-generator-test-component-typescript/src/index.ts @@ -17,5 +17,5 @@ export { FCComponent, EmptyComponent, MixedComponent, - RequiredChildrenComponent, + RequiredChildrenComponent }; diff --git a/dash/extract-meta.js b/dash/extract-meta.js index c98133b6c3..431977b873 100755 --- a/dash/extract-meta.js +++ b/dash/extract-meta.js @@ -726,22 +726,23 @@ function gatherComponents(sources, components = {}) { } } - const isArrowFunction = typeSymbol.declarations && typeSymbol.declarations[0].kind === ts.SyntaxKind.ArrowFunction + const isArrowFunction = ts.isArrowFunction(declaration) const isClass = ts.isClassDeclaration(declaration); + const isFunction = ts.isFunctionDeclaration(declaration) - if (!isArrowFunction && !isClass) { + if (!isArrowFunction && !isClass && !isFunction) { // we do not care about these exports return null } - // if (isArrowFunction) { - // const signature = checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; - // const returnType = checker.typeToString(signature.getReturnType()); - // if (returnType !== 'Element') { - // // Not JSX so no need to classifiy as compnent - // return null; - // } - // } + if (isArrowFunction || isFunction) { + const signature = checker.getSignaturesOfType(type, ts.SignatureKind.Call)[0]; + const returnType = checker.typeToString(signature.getReturnType()); + if (!['Element', 'any', 'null'].includes(returnType)) { + // Not JSX so no need to classifiy as compnent + return null; + } + } let defaultProps = getDefaultProps(typeSymbol, source); const propsType = getPropsForFunctionalComponent(type);