From 198736a5f19ea02cb58d7bcf2dfa0e77db53614e Mon Sep 17 00:00:00 2001 From: mmews-n4 Date: Mon, 22 Apr 2024 09:22:52 +0200 Subject: [PATCH] GH-2614: N4JSX should accept functions that return ReactNodes (#2621) * allow ReactNodes as a return type for jsx component functions * adjust tests * adjust tests --- .../n4js/tooling/react/ReactHelper.java | 16 ++++++++ .../validators/N4JSXValidator.xtend | 6 +-- .../xt-tests/FunctionReactNode.n4jsx.xt | 37 +++++++++++++++++++ ...nts_Referring_to_React_Components.n4jsx.xt | 2 +- .../xt-tests/react/index.n4jsd | 10 +++++ .../reactWithModifications/index.n4jsd | 11 ++++++ .../react-implementations/valid/index.n4jsd | 11 ++++++ .../n4jsx/ClassComponentDescriptions.n4jsx.xt | 4 +- .../FunctionComponentDescriptions.n4jsx.xt | 2 +- 9 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 tests/org.eclipse.n4js.spec.tests/xt-tests/FunctionReactNode.n4jsx.xt diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/tooling/react/ReactHelper.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/tooling/react/ReactHelper.java index bd8d130f36..83950838e0 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/tooling/react/ReactHelper.java +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/tooling/react/ReactHelper.java @@ -77,6 +77,8 @@ public class ReactHelper { /***/ public final static String REACT_COMPONENT = "Component"; /***/ + public final static String REACT_NODE = "ReactNode"; + /***/ public final static String REACT_ELEMENT = "ReactElement"; /***/ public final static String REACT_FRAGMENT_NAME = "Fragment"; @@ -174,6 +176,20 @@ public IdentifiableElement getJsxBackendFragmentComponent(Resource resource) { return null; } + /** + * Look up React.Node in the index. + * + * @param context + * the EObject serving the context to look for React.Node. + */ + public TClassifier lookUpReactNode(EObject context) { + // val reactElement = lookUpReactClassifier(context, REACT_ELEMENT, TInterface) + // if (reactElement !== null) { + // return reactElement; + // } + return lookUpReactClassifier_OLD(context, REACT_NODE, TInterface.class); + } + /** * Look up React.Element in the index. * diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/N4JSXValidator.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/N4JSXValidator.xtend index 1372d629f3..215365dd8c 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/N4JSXValidator.xtend +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/N4JSXValidator.xtend @@ -221,11 +221,11 @@ class N4JSXValidator extends AbstractN4JSDeclarativeValidator { * See Req. IDE-241116 */ def private void checkFunctionTypeExprOrRef(JSXElement jsxElem, FunctionTypeExprOrRef exprTypeRef) { - val tReactElement = reactHelper.lookUpReactElement(jsxElem); - if (tReactElement === null) + val tReactNode = reactHelper.lookUpReactNode(jsxElem); + if (tReactNode === null) return; - val expectedReturnTypeRef = TypeUtils.createTypeRef(tReactElement, TypingStrategy.DEFAULT, true); + val expectedReturnTypeRef = TypeUtils.createTypeRef(tReactNode, TypingStrategy.DEFAULT, true); val expr = jsxElem.jsxElementName.expression; val G = expr.newRuleEnvironment; diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/FunctionReactNode.n4jsx.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/FunctionReactNode.n4jsx.xt new file mode 100644 index 0000000000..25ca715af7 --- /dev/null +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/FunctionReactNode.n4jsx.xt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ + +/* XPECT_SETUP org.eclipse.n4js.spec.tests.SpecXtTest + + Workspace { + Project "N4JSXSpecTest" { + Folder "src" { + ThisFile {} + } + File "package.json" { from="../package_default.json" } + } + Project "react" { + File "index.n4jsd" { from = "react/index.n4jsd" } + File "package.json" { from = "react/package.json" } + } + } + + END_SETUP +*/ + +import * as React from "react/index"; + + +function Comp(): React.ReactNode { + return null; +} +// XPECT noerrors --> + diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/Req241116_JSXElements_Referring_to_React_Components.n4jsx.xt b/tests/org.eclipse.n4js.spec.tests/xt-tests/Req241116_JSXElements_Referring_to_React_Components.n4jsx.xt index 1c30f43258..049fa4f774 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/Req241116_JSXElements_Referring_to_React_Components.n4jsx.xt +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/Req241116_JSXElements_Referring_to_React_Components.n4jsx.xt @@ -40,7 +40,7 @@ function MyNormalFunction(props: ~React.ComponentProps with {myOtherProp: string return "My Normal Function"; } -// XPECT errors --> "Expecting a function returning a value of type ReactElement but the return type is string." at "MyNormalFunction" +// XPECT noerrors --> "Expecting a function returning a value of type ReactElement but the return type is string." at "MyNormalFunction" class MyReactClassComponent extends React.Component<~React.ComponentProps with {prop: int}, Object> { diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/react/index.n4jsd b/tests/org.eclipse.n4js.spec.tests/xt-tests/react/index.n4jsd index f1213f4145..e759a7416f 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/react/index.n4jsd +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/react/index.n4jsd @@ -45,6 +45,16 @@ export external public abstract class PureComponent { } + +export external public type ReactText = string | number; +export external public type ReactChild = ReactElement | ReactText; + +export external public type ReactNodeArray = ReactNode[]; +export external public type ReactFragment = Object | ReactNodeArray; +export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal | */ boolean; + + + // mor TODO IDE-2323 // public static createElement( // type: union{string, {function(PropsT?): ReactElement}, constructor{Component}}, diff --git a/tests/org.eclipse.n4js.spec.tests/xt-tests/reactWithModifications/index.n4jsd b/tests/org.eclipse.n4js.spec.tests/xt-tests/reactWithModifications/index.n4jsd index 170dd795b5..53daadd984 100644 --- a/tests/org.eclipse.n4js.spec.tests/xt-tests/reactWithModifications/index.n4jsd +++ b/tests/org.eclipse.n4js.spec.tests/xt-tests/reactWithModifications/index.n4jsd @@ -49,6 +49,17 @@ export external public abstract class Component | ReactText; + +export external public type ReactNodeArray = ReactNode[]; +export external public type ReactFragment = Object | ReactNodeArray; +export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal | */ boolean; + + + + export external public abstract class PureComponent extends Component { } diff --git a/tests/org.eclipse.n4js.xpect.tests/react-implementations/valid/index.n4jsd b/tests/org.eclipse.n4js.xpect.tests/react-implementations/valid/index.n4jsd index f1213f4145..76bc3c4855 100644 --- a/tests/org.eclipse.n4js.xpect.tests/react-implementations/valid/index.n4jsd +++ b/tests/org.eclipse.n4js.xpect.tests/react-implementations/valid/index.n4jsd @@ -45,6 +45,17 @@ export external public abstract class PureComponent { } + +export external public type ReactText = string | number; +export external public type ReactChild = ReactElement | ReactText; + +export external public type ReactNodeArray = ReactNode[]; +export external public type ReactFragment = Object | ReactNodeArray; +export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal | */ boolean; + + + + // mor TODO IDE-2323 // public static createElement( // type: union{string, {function(PropsT?): ReactElement}, constructor{Component}}, diff --git a/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/ClassComponentDescriptions.n4jsx.xt b/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/ClassComponentDescriptions.n4jsx.xt index 233ba50a92..a0b16d6a94 100644 --- a/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/ClassComponentDescriptions.n4jsx.xt +++ b/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/ClassComponentDescriptions.n4jsx.xt @@ -43,7 +43,7 @@ export public class LowerComponent extends React.Component<~React.ComponentProps /* XPECT scope at 'React.<|>Component' --- -Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactElement +Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactChild, ReactElement, ReactFragment, ReactNode, ReactNodeArray, ReactText --- */ export public class MyComponent extends React.Component<~React.ComponentProps with {aProp: string}, String> { @@ -72,7 +72,7 @@ export public class MyComponent extends React.Component<~React.ComponentProps wi __proto__, arguments, console, global, constText, constructor, decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, eval, hasOwnProperty, index.Component, index.ComponentProps, index.Fragment, index.PropConstraint, - index.PropType, index.PropTypes, import, index.PureComponent, index.ReactElement, index.cloneElement, index.createElement, + index.PropType, index.PropTypes, import, index.PureComponent, index.ReactChild, index.ReactElement, index.ReactFragment, index.ReactNode, index.ReactNodeArray, index.ReactText, index.cloneElement, index.createElement, index.isValidElement, index.renderToStaticMarkup, index.renderToString, isFinite, isNaN, isPrototypeOf, null, parseFloat, parseInt, propertyIsEnumerable, toLocaleString, toString, undefined, valueOf, void diff --git a/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/FunctionComponentDescriptions.n4jsx.xt b/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/FunctionComponentDescriptions.n4jsx.xt index f88bbda91c..d57ef3396b 100644 --- a/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/FunctionComponentDescriptions.n4jsx.xt +++ b/tests/org.eclipse.n4js.xpect.tests/xt-tests/scoping/n4jsx/FunctionComponentDescriptions.n4jsx.xt @@ -39,7 +39,7 @@ class Props { } /* XPECT scope at 'React.<|>ReactElement' --- -Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactElement +Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactChild, ReactElement, ReactFragment, ReactNode, ReactNodeArray, ReactText --- */ export public function NamedFnComponent(props: Props): React.ReactElement { return (
{props.myProp}
);