diff --git a/analysis/examples/larger-project/package.json b/analysis/examples/larger-project/package.json index 1426ee18c6..c414245957 100644 --- a/analysis/examples/larger-project/package.json +++ b/analysis/examples/larger-project/package.json @@ -14,6 +14,6 @@ }, "dependencies": { "@glennsl/bs-json": "^5.0.4", - "@rescript/react": "^0.10.3" + "@rescript/react": "workspace:^" } } diff --git a/package.json b/package.json index aca5a88df1..b216aae31a 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "tests/analysis_tests/**", "tests/docstring_tests", "tests/gentype_tests/**", + "tests/tests", "tests/tools_tests", "scripts/res" ], diff --git a/packages/playground/package.json b/packages/playground/package.json index c36b14bd40..283f909917 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -9,7 +9,7 @@ "upload-bundle": "node scripts/upload_bundle.mjs" }, "dependencies": { - "@rescript/react": "^0.13.1", + "@rescript/react": "workspace:^", "rescript": "workspace:^" }, "devDependencies": { diff --git a/packages/playground/src/App.res b/packages/playground/src/App.res index e69de29bb2..8b13789179 100644 --- a/packages/playground/src/App.res +++ b/packages/playground/src/App.res @@ -0,0 +1 @@ + diff --git a/scripts/format.sh b/scripts/format.sh index a1bbd91607..aa335f3e79 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -4,7 +4,7 @@ shopt -s extglob dune build @fmt --auto-promote -files=$(find runtime tests -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*") +files=$(find runtime tests packages -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*") ./cli/rescript.js format $files yarn format diff --git a/scripts/format_check.sh b/scripts/format_check.sh index b7daf67d4f..a6e1085c74 100755 --- a/scripts/format_check.sh +++ b/scripts/format_check.sh @@ -17,7 +17,7 @@ case "$(uname -s)" in fi echo "Checking ReScript code formatting..." - files=$(find runtime tests -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*") + files=$(find runtime tests packages -type f \( -name "*.res" -o -name "*.resi" \) ! -name "syntaxErrors*" ! -name "generated_mocha_test.res" ! -path "tests/syntax_tests*" ! -path "tests/analysis_tests/tests*" ! -path "*/node_modules/*") if ./cli/rescript.js format -check $files; then printf "${successGreen}✅ ReScript code formatting ok.${reset}\n" else diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt index 6104d9e62b..61ee5856d9 100644 --- a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt +++ b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt @@ -407,7 +407,7 @@ addValueDeclaration +make Hooks.res:63:6 path:+Hooks.RenderPropRequiresConversion addRecordLabelDeclaration name Hooks.res:1:16 path:+Hooks.vehicle addRecordLabelDeclaration vehicle Hooks.res:4:12 path:+Hooks.props - addValueReference Hooks.res:5:26 --> React.res:134:0 + addValueReference Hooks.res:5:26 --> React.res:135:0 addTypeReference Hooks.res:10:29 --> Hooks.res:1:16 addValueReference Hooks.res:10:29 --> Hooks.res:4:12 addValueReference Hooks.res:10:75 --> Hooks.res:5:7 diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/package.json b/tests/analysis_tests/tests-reanalyze/deadcode/package.json index ec9305fdb8..a61fbc97ed 100644 --- a/tests/analysis_tests/tests-reanalyze/deadcode/package.json +++ b/tests/analysis_tests/tests-reanalyze/deadcode/package.json @@ -6,7 +6,7 @@ "clean": "rescript clean -with-deps" }, "dependencies": { - "@rescript/react": "link:../../../dependencies/rescript-react", + "@rescript/react": "workspace:^", "rescript": "workspace:^" } } diff --git a/tests/analysis_tests/tests/package.json b/tests/analysis_tests/tests/package.json index 2ada6a7cb0..fd8de6a7c1 100644 --- a/tests/analysis_tests/tests/package.json +++ b/tests/analysis_tests/tests/package.json @@ -6,7 +6,7 @@ "clean": "rescript clean -with-deps" }, "dependencies": { - "@rescript/react": "link:../../dependencies/rescript-react", + "@rescript/react": "workspace:^", "rescript": "workspace:^" } } diff --git a/tests/analysis_tests/tests/src/expected/CompletionExpressions.res.txt b/tests/analysis_tests/tests/src/expected/CompletionExpressions.res.txt index bfea51f5fc..aadc323691 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionExpressions.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionExpressions.res.txt @@ -865,13 +865,13 @@ ContextPath CArgument Value[fnTakingCallback]($2) ContextPath Value[fnTakingCallback] Path fnTakingCallback [{ - "label": "event => event", + "label": "mouse => mouse", "kind": 12, "tags": [], "detail": "ReactEvent.Mouse.t => unit", "documentation": null, "sortText": "A", - "insertText": "${1:event} => ${0:event}", + "insertText": "${1:mouse} => ${0:mouse}", "insertTextFormat": 2 }] diff --git a/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt b/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt index d9fe7877f9..2c46bcb49c 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionInferValues.res.txt @@ -250,16 +250,7 @@ ContextPath CArgument CArgument Value[reactEventFn]($0)($0) ContextPath CArgument Value[reactEventFn]($0) ContextPath Value[reactEventFn] Path reactEventFn -CPPipe pathFromEnv:ReactEvent.Mouse found:false -Path ReactEvent.Mouse.pr -Path pr -[{ - "label": "ReactEvent.Mouse.preventDefault", - "kind": 12, - "tags": [], - "detail": "t => unit", - "documentation": null - }] +[] Complete src/CompletionInferValues.res 41:50 posCursor:[41:50] posNoWhite:[41:49] Found expr:[41:11->41:56] @@ -275,17 +266,7 @@ Path event ContextPath CArgument CJsxPropValue [div] onMouseEnter($0) ContextPath CJsxPropValue [div] onMouseEnter Path ReactDOM.domProps -Path JsxDOM.domProps -CPPipe pathFromEnv:JsxEvent.Mouse found:false -Path JsxEvent.Mouse.pr -Path pr -[{ - "label": "JsxEvent.Mouse.preventDefault", - "kind": 12, - "tags": [], - "detail": "t => unit", - "documentation": null - }] +[] Complete src/CompletionInferValues.res 44:50 posCursor:[44:50] posNoWhite:[44:49] Found expr:[44:11->44:56] @@ -832,22 +813,7 @@ ContextPath CArgument CArgument Value[fn2](~cb)($0) ContextPath CArgument Value[fn2](~cb) ContextPath Value[fn2] Path fn2 -CPPipe pathFromEnv:ReactDOM.Client.Root found:false -Path ReactDOM.Client.Root. -Path -[{ - "label": "ReactDOM.Client.Root.unmount", - "kind": 12, - "tags": [], - "detail": "(t, unit) => unit", - "documentation": null - }, { - "label": "ReactDOM.Client.Root.render", - "kind": 12, - "tags": [], - "detail": "(t, React.element) => unit", - "documentation": null - }] +[] Complete src/CompletionInferValues.res 139:30 posCursor:[139:30] posNoWhite:[139:29] Found expr:[139:3->139:33] @@ -974,22 +940,7 @@ ContextPath CArgument CArgument Value[CompletionSupport2, makeRenderer](~render) ContextPath CArgument Value[CompletionSupport2, makeRenderer](~render) ContextPath Value[CompletionSupport2, makeRenderer] Path CompletionSupport2.makeRenderer -CPPipe pathFromEnv:ReactDOM.Client.Root found:false -Path ReactDOM.Client.Root. -Path -[{ - "label": "ReactDOM.Client.Root.unmount", - "kind": 12, - "tags": [], - "detail": "(t, unit) => unit", - "documentation": null - }, { - "label": "ReactDOM.Client.Root.render", - "kind": 12, - "tags": [], - "detail": "(t, React.element) => unit", - "documentation": null - }] +[] Hover src/CompletionInferValues.res 160:27 Nothing at that position. Now trying to use completion. diff --git a/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt b/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt index 412e810c27..0f07350735 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionJsx.res.txt @@ -629,14 +629,8 @@ Completable: Cjsx([h1], hidd, [hidd]) Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib Path ReactDOM.domProps -Path JsxDOM.domProps -[{ - "label": "hidden", - "kind": 4, - "tags": [], - "detail": "bool", - "documentation": null - }] +[completing-lowercase-jsx] could not find element props to complete from. +[] Complete src/CompletionJsx.res 61:30 posCursor:[61:30] posNoWhite:[61:28] Found expr:[61:3->61:29] diff --git a/tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt b/tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt index 686f4f5ec9..8d83639661 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt @@ -223,20 +223,7 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath CJsxPropValue [div] muted Path ReactDOM.domProps -Path JsxDOM.domProps -[{ - "label": "true", - "kind": 4, - "tags": [], - "detail": "bool", - "documentation": null - }, { - "label": "false", - "kind": 4, - "tags": [], - "detail": "bool", - "documentation": null - }] +[] Complete src/CompletionJsxProps.res 18:29 posCursor:[18:29] posNoWhite:[18:28] Found expr:[18:11->18:32] @@ -246,17 +233,7 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath CJsxPropValue [div] onMouseEnter Path ReactDOM.domProps -Path JsxDOM.domProps -[{ - "label": "event => event", - "kind": 12, - "tags": [], - "detail": "JsxEvent.Mouse.t => unit", - "documentation": null, - "sortText": "A", - "insertText": "{${1:event} => ${0:event}}", - "insertTextFormat": 2 - }] +[] Complete src/CompletionJsxProps.res 22:52 posCursor:[22:52] posNoWhite:[22:51] Found expr:[22:11->22:52] diff --git a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt index dcf268e218..8dae38a68c 100644 --- a/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt +++ b/tests/analysis_tests/tests/src/expected/CompletionPipeChain.res.txt @@ -470,16 +470,7 @@ Path support CPPipe pathFromEnv:CompletionSupport.Nested found:false Path CompletionSupport.Nested.root Path root -CPPipe pathFromEnv:ReactDOM.Client.Root found:false -Path ReactDOM.Client.Root.ren -Path ren -[{ - "label": "ReactDOM.Client.Root.render", - "kind": 12, - "tags": [], - "detail": "(t, React.element) => unit", - "documentation": null - }] +[] Complete src/CompletionPipeChain.res 88:16 posCursor:[88:16] posNoWhite:[88:15] Found expr:[76:15->93:1] @@ -496,16 +487,7 @@ Resolved opens 1 Stdlib ContextPath Value[root]->ren ContextPath Value[root] Path root -CPPipe pathFromEnv:ReactDOM.Client.Root found:false -Path ReactDOM.Client.Root.ren -Path ren -[{ - "label": "ReactDOM.Client.Root.render", - "kind": 12, - "tags": [], - "detail": "(t, React.element) => unit", - "documentation": null - }] +[] Complete src/CompletionPipeChain.res 95:20 posCursor:[95:20] posNoWhite:[95:19] Found expr:[95:3->95:21] diff --git a/tests/analysis_tests/tests/src/expected/Div.res.txt b/tests/analysis_tests/tests/src/expected/Div.res.txt index deb951ada8..966984d6bc 100644 --- a/tests/analysis_tests/tests/src/expected/Div.res.txt +++ b/tests/analysis_tests/tests/src/expected/Div.res.txt @@ -8,12 +8,6 @@ Completable: Cjsx([div], dangerous, [dangerous]) Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib Path ReactDOM.domProps -Path JsxDOM.domProps -[{ - "label": "dangerouslySetInnerHTML", - "kind": 4, - "tags": [], - "detail": "{\"__html\": string}", - "documentation": null - }] +[completing-lowercase-jsx] could not find element props to complete from. +[] diff --git a/tests/analysis_tests/tests/src/expected/Fragment.res.txt b/tests/analysis_tests/tests/src/expected/Fragment.res.txt index 689d63155e..f23606a816 100644 --- a/tests/analysis_tests/tests/src/expected/Fragment.res.txt +++ b/tests/analysis_tests/tests/src/expected/Fragment.res.txt @@ -1,6 +1,6 @@ Hover src/Fragment.res 6:19 -{"contents": {"kind": "markdown", "value": "```rescript\nReact.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype React.component<'props> = Jsx.component<'props>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype SectionHeader.props<'children> = {children: 'children}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Fragment.res%22%2C1%2C2%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.element = Jsx.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n"}} +{"contents": {"kind": "markdown", "value": "```rescript\nReact.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype SectionHeader.props<'children> = {children: 'children}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Fragment.res%22%2C1%2C2%5D)\n"}} Hover src/Fragment.res 9:56 -{"contents": {"kind": "markdown", "value": "```rescript\nReact.component\n```\n\n---\n\n```\n \n```\n```rescript\ntype React.component<'props> = Jsx.component<'props>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.fragmentProps = {children?: element}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C41%2C0%5D)\n"}} +{"contents": {"kind": "markdown", "value": "```rescript\nReact.component\n```"}} diff --git a/tests/analysis_tests/tests/src/expected/Hover.res.txt b/tests/analysis_tests/tests/src/expected/Hover.res.txt index a68c232167..78f9ffbe91 100644 --- a/tests/analysis_tests/tests/src/expected/Hover.res.txt +++ b/tests/analysis_tests/tests/src/expected/Hover.res.txt @@ -253,7 +253,7 @@ Path }] Hover src/Hover.res 197:4 -{"contents": {"kind": "markdown", "value": "```rescript\nCompV4.props => React.element\n```\n\n---\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {n?: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.element = Jsx.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n"}} +{"contents": {"kind": "markdown", "value": "```rescript\nCompV4.props => React.element\n```\n\n---\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {n?: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n"}} Hover src/Hover.res 202:16 {"contents": {"kind": "markdown", "value": "```rescript\nuseR\n```\n\n---\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)\n"}} diff --git a/tests/analysis_tests/tests/src/expected/Jsx2.res.txt b/tests/analysis_tests/tests/src/expected/Jsx2.res.txt index 66920cb633..5f60fdf6ea 100644 --- a/tests/analysis_tests/tests/src/expected/Jsx2.res.txt +++ b/tests/analysis_tests/tests/src/expected/Jsx2.res.txt @@ -356,13 +356,7 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath Type[React, e] Path React.e -[{ - "label": "element", - "kind": 22, - "tags": [], - "detail": "type element", - "documentation": {"kind": "markdown", "value": "```rescript\ntype element = Jsx.element\n```"} - }] +[] Complete src/Jsx2.res 96:20 posCursor:[96:20] posNoWhite:[96:19] Found pattern:[96:7->99:6] @@ -472,17 +466,7 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath CJsxPropValue [div] x Path ReactDOM.domProps -Path JsxDOM.domProps -[{ - "label": "\"\"", - "kind": 12, - "tags": [], - "detail": "string", - "documentation": null, - "sortText": "A", - "insertText": "{\"$0\"}", - "insertTextFormat": 2 - }] +[] Complete src/Jsx2.res 150:21 posCursor:[150:21] posNoWhite:[150:20] Found expr:[150:11->150:32] diff --git a/tests/analysis_tests/tests/src/expected/Jsx2.resi.txt b/tests/analysis_tests/tests/src/expected/Jsx2.resi.txt index c34ab6fca5..c70c9688c4 100644 --- a/tests/analysis_tests/tests/src/expected/Jsx2.resi.txt +++ b/tests/analysis_tests/tests/src/expected/Jsx2.resi.txt @@ -12,13 +12,7 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath Type[React, e] Path React.e -[{ - "label": "element", - "kind": 22, - "tags": [], - "detail": "type element", - "documentation": {"kind": "markdown", "value": "```rescript\ntype element = Jsx.element\n```"} - }] +[] Complete src/Jsx2.resi 10:18 posCursor:[10:18] posNoWhite:[10:17] Found type:[10:11->10:18] @@ -28,11 +22,5 @@ Package opens Stdlib.place holder Pervasives.JsxModules.place holder Resolved opens 1 Stdlib ContextPath Type[React, e] Path React.e -[{ - "label": "element", - "kind": 22, - "tags": [], - "detail": "type element", - "documentation": {"kind": "markdown", "value": "```rescript\ntype element = Jsx.element\n```"} - }] +[] diff --git a/tests/analysis_tests/tests/src/expected/JsxV4.res.txt b/tests/analysis_tests/tests/src/expected/JsxV4.res.txt index ba54c5d047..0319c267b3 100644 --- a/tests/analysis_tests/tests/src/expected/JsxV4.res.txt +++ b/tests/analysis_tests/tests/src/expected/JsxV4.res.txt @@ -17,7 +17,7 @@ Path M4.make }] Hover src/JsxV4.res 14:9 -{"contents": {"kind": "markdown", "value": "```rescript\nReact.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype React.component<'props> = Jsx.component<'props>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype M4.props<'first, 'fun, 'second> = {\n first: 'first,\n fun?: 'fun,\n second?: 'second,\n}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22JsxV4.res%22%2C3%2C2%5D)\n\n---\n Doc Comment For M4 "}} +{"contents": {"kind": "markdown", "value": "```rescript\nReact.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype M4.props<'first, 'fun, 'second> = {\n first: 'first,\n fun?: 'fun,\n second?: 'second,\n}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22JsxV4.res%22%2C3%2C2%5D)\n\n---\n Doc Comment For M4 "}} Create Interface src/JsxV4.res module M4: { diff --git a/tests/dependencies/rescript-react/package.json b/tests/dependencies/rescript-react/package.json index cd0136bdd3..94de119333 100644 --- a/tests/dependencies/rescript-react/package.json +++ b/tests/dependencies/rescript-react/package.json @@ -1,4 +1,8 @@ { - "name": "@tests/rescript-react", - "private": true + "name": "@rescript/react", + "version": "0.14.0-rc.1", + "private": true, + "dependencies": { + "rescript": "workspace:^" + } } diff --git a/tests/dependencies/rescript-react/src/React.res b/tests/dependencies/rescript-react/src/React.res index 0beaa59080..f1f6831518 100644 --- a/tests/dependencies/rescript-react/src/React.res +++ b/tests/dependencies/rescript-react/src/React.res @@ -5,6 +5,7 @@ type element = Jsx.element external float: float => element = "%identity" external int: int => element = "%identity" external string: string => element = "%identity" +external promise: promise => element = "%identity" external array: array => element = "%identity" @@ -250,6 +251,9 @@ external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = @module("react") external useContext: Context.t<'any> => 'any = "useContext" +@module("react") +external usePromise: promise<'a> => 'a = "use" + @module("react") external useRef: 'value => ref<'value> = "useRef" @module("react") @@ -309,10 +313,9 @@ external useImperativeHandle7: ( @module("react") external useId: unit => string = "useId" -@module("react") external useDeferredValue: 'value => 'value = "useDeferredValue" - +/** `useDeferredValue` is a React Hook that lets you defer updating a part of the UI. */ @module("react") -external useTransition: unit => (bool, (unit => unit) => unit) = "useTransition" +external useDeferredValue: ('value, ~initialValue: 'value=?) => 'value = "useDeferredValue" @module("react") external useInsertionEffectOnEveryRender: (unit => option unit>) => unit = @@ -405,3 +408,36 @@ external setDisplayName: (component<'props>, string) => unit = "displayName" @get @return(nullable) external displayName: component<'props> => option = "displayName" + +// Actions + +type transitionFunction = unit => promise + +type transitionStartFunction = transitionFunction => unit + +/** `useTransition` is a React Hook that lets you render a part of the UI in the background. */ +@module("react") +external useTransition: unit => (bool, transitionStartFunction) = "useTransition" + +type action<'state, 'payload> = ('state, 'payload) => promise<'state> + +type formAction<'formData> = 'formData => promise + +/** `useActionState` is a Hook that allows you to update state based on the result of a form action. */ +@module("react") +external useActionState: ( + action<'state, 'payload>, + 'state, + ~permalink: string=?, +) => ('state, formAction<'payload>, bool) = "useActionState" + +/** `useOptimistic` is a React Hook that lets you optimistically update the UI. */ +@module("react") +external useOptimistic: ( + 'state, + ~updateFn: ('state, 'action) => 'state=?, +) => ('state, 'action => unit) = "useOptimistic" + +/** `act` is a test helper to apply pending React updates before making assertions. */ +@module("react") +external act: (unit => promise) => promise = "act" diff --git a/tests/dependencies/rescript-react/src/ReactDOM.res b/tests/dependencies/rescript-react/src/ReactDOM.res index 04c39c2647..fbd564bf4d 100644 --- a/tests/dependencies/rescript-react/src/ReactDOM.res +++ b/tests/dependencies/rescript-react/src/ReactDOM.res @@ -25,6 +25,52 @@ module Client = { external hydrateRoot: (Dom.element, React.element) => Root.t = "hydrateRoot" } +// Very rudimentary form data bindings +module FormData = { + type t + type file + + type formValue = + | String(string) + | File(file) + + @new external make: unit => t = "FormData" + + @send external append: (t, string, ~filename: string=?) => unit = "append" + @send external delete: (t, string) => unit = "delete" + @return(nullable) @send external getUnsafe: (t, string) => option<'a> = "get" + @send external getAllUnsafe: (t, string) => array<'a> = "getAll" + + let getString = (formData, name) => { + switch formData->getUnsafe(name) { + | Some(value) => Js.typeof(value) === "string" ? Some(value) : None + | _ => None + } + } + + external _asFile: 'a => file = "%identity" + + let getFile = (formData, name) => { + switch formData->getUnsafe(name) { + | Some(value) => Js.typeof(value) === "string" ? None : Some(value->_asFile) + | _ => None + } + } + + let getAll = (t, string) => { + t + ->getAllUnsafe(string) + ->Js.Array2.map(value => { + Js.typeof(value) === "string" ? String(value) : File(value->_asFile) + }) + } + + @send external set: (string, string) => unit = "set" + @send external has: string => bool = "has" + // @send external keys: t => Iterator.t = "keys"; + // @send external values: t => Iterator.t = "values"; +} + @module("react-dom") external createPortal: (React.element, Dom.element) => React.element = "createPortal" @@ -37,12 +83,142 @@ type domRef = JsxDOM.domRef module Ref = { type t = domRef type currentDomRef = React.ref> - type callbackDomRef = Js.nullable => unit + type callbackDomRef = Js.nullable => option unit> external domRef: currentDomRef => domRef = "%identity" external callbackDomRef: callbackDomRef => domRef = "%identity" } +// Hooks + +type formStatus<'state> = { + /** If true, this means the parent
is pending submission. Otherwise, false. */ + pending: bool, + /** An object implementing the FormData interface that contains the data the parent is submitting. If there is no active submission or no parent , it will be null. */ + data: FormData.t, + /** This represents whether the parent is submitting with either a GET or POST HTTP method. By default, a will use the GET method and can be specified by the method property. */ + method: [#get | #post], + /** A reference to the function passed to the action prop on the parent . If there is no parent , the property is null. If there is a URI value provided to the action prop, or no action prop specified, status.action will be null. */ + action: React.action<'state, FormData.t>, +} + +external formAction: React.formAction => string = "%identity" + +/** `useFormStatus` is a Hook that gives you status information of the last form submission. */ +@module("react-dom") +external useFormStatus: unit => formStatus<'state> = "useFormStatus" + +// Resource Preloading APIs + +/** The CORS policy to use. */ +type crossOrigin = [ + | #anonymous + | #"use-credentials" +] + +/** The Referrer header to send when fetching. */ +type referrerPolicy = [ + | #"referrer-when-downgrade" + | #"no-referrer" + | #origin + | #"origin-when-cross-origin" + | #"unsafe-url" +] + +/** Suggests a relative priority for fetching the resource. */ +type fetchPriority = [#auto | #high | #low] + +/** `prefetchDNS` lets you eagerly look up the IP of a server that you expect to load resources from. */ +@module("react-dom") +external prefetchDNS: string => unit = "prefetchDNS" + +/** `preconnect` lets you eagerly connect to a server that you expect to load resources from. */ +@module("react-dom") +external preconnect: string => unit = "preconnect" + +type preloadOptions = { + /** The type of resource. */ + @as("as") + as_: [ + | #audio + | #document + | #embed + | #fetch + | #font + | #image + | #object + | #script + | #style + | #track + | #video + | #worker + ], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** The Referrer header to send when fetching. */ + referrerPolicy?: referrerPolicy, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + /** The MIME type of the resource. */ + @as("type") + type_?: string, + /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ + nonce?: string, + /** Suggests a relative priority for fetching the resource. */ + fetchPriority?: fetchPriority, + /** For use only with as: "image". Specifies the source set of the image. */ + imageSrcSet?: string, + /** For use only with as: "image". Specifies the sizes of the image. */ + imageSizes?: string, +} + +/** `preload` lets you eagerly fetch a resource such as a stylesheet, font, or external script that you expect to use. */ +@module("react-dom") +external preload: (string, preloadOptions) => unit = "preload" + +type preloadModuleOptions = { + /** The type of resource. */ + @as("as") + as_: [#script], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ + nonce?: string, +} + +/** `preloadModule` lets you eagerly fetch an ESM module that you expect to use. */ +@module("react-dom") +external preloadModule: (string, preloadModuleOptions) => unit = "preloadModule" + +type preinitOptions = { + /** The type of resource. */ + @as("as") + as_: [#script | #style], + /** Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. */ + precedence?: [#reset | #low | #medium | #high], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** The Referrer header to send when fetching. */ + referrerPolicy?: referrerPolicy, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + nonce?: string, + /** Suggests a relative priority for fetching the resource. */ + fetchPriority?: fetchPriority, +} + +/** `preinit` lets you eagerly fetch and evaluate a stylesheet or external script. */ +@module("react-dom") +external preinit: (string, preinitOptions) => unit = "preinit" + +/** To preinit an ESM module, call the `preinitModule` function from react-dom. */ +@module("react-dom") +external preinitModule: (string, preloadModuleOptions) => unit = "preinitModule" + +// Runtime + type domProps = JsxDOM.domProps @variadic @module("react") diff --git a/tests/tests/src/reactDOMServer.res b/tests/dependencies/rescript-react/src/ReactDOMServer.res similarity index 100% rename from tests/tests/src/reactDOMServer.res rename to tests/dependencies/rescript-react/src/ReactDOMServer.res diff --git a/tests/dependencies/rescript-react/src/ReactDOMStatic.res b/tests/dependencies/rescript-react/src/ReactDOMStatic.res new file mode 100644 index 0000000000..7988fbb754 --- /dev/null +++ b/tests/dependencies/rescript-react/src/ReactDOMStatic.res @@ -0,0 +1,30 @@ +type abortSignal // WebAPI.EventAPI.abortSignal + +type nodeStream // NodeJs.Stream.stream + +type readableStream // WebAPI.FileAPI.readableStream + +type prerenderOptions<'error> = { + bootstrapScriptContent?: string, + bootstrapScripts?: array, + bootstrapModules?: array, + identifierPrefix?: string, + namespaceURI?: string, + onError?: 'error => unit, + progressiveChunkSize?: int, + signal?: abortSignal, +} + +type staticResult = {prelude: readableStream} + +@module("react-dom/static") +external prerender: (React.element, ~options: prerenderOptions<'error>=?) => promise = + "prerender" + +type staticResultNode = {prelude: nodeStream} + +@module("react-dom/static") +external prerenderToNodeStream: ( + React.element, + ~options: prerenderOptions<'error>=?, +) => promise = "prerenderToNodeStream" diff --git a/tests/tests/src/reactTestUtils.res b/tests/dependencies/rescript-react/src/ReactTestUtils.res similarity index 88% rename from tests/tests/src/reactTestUtils.res rename to tests/dependencies/rescript-react/src/ReactTestUtils.res index 74b7e62450..a6c2b5e2a3 100644 --- a/tests/tests/src/reactTestUtils.res +++ b/tests/dependencies/rescript-react/src/ReactTestUtils.res @@ -25,7 +25,7 @@ let actAsync = func => { external isElement: 'element => bool = "isElement" @module("react-dom/test-utils") -external isElementOfType: ('element, React.component<'props>) => bool = "isElement" +external isElementOfType: ('element, React.component<'props>) => bool = "isElementOfType" @module("react-dom/test-utils") external isDOMComponent: 'element => bool = "isDOMComponent" @@ -94,8 +94,6 @@ external appendChild: (Dom.element, Dom.element) => Dom.element = "appendChild" let querySelectorAll = (element, string) => Js.Array.from(querySelectorAll(element, string)) module DOM = { - open Belt - @return(nullable) @get external value: Dom.element => option = "value" @@ -104,30 +102,33 @@ module DOM = { let findByAllSelector = (element, selector) => querySelectorAll(element, selector) let findBySelectorAndTextContent = (element, selector, content) => - querySelectorAll(element, selector)->Array.getBy(node => node->textContent === content) + querySelectorAll(element, selector)->Js.Array2.find(node => node->textContent === content) let findBySelectorAndPartialTextContent = (element, selector, content) => - querySelectorAll(element, selector)->Array.getBy(node => + querySelectorAll(element, selector)->Js.Array2.find(node => node->textContent->Js.String2.includes(content) ) } let prepareContainer = (container: ref>, ()) => { - open Belt - let containerElement = document->createElement("div") - let _ = document->body->Option.map(body => body->appendChild(containerElement)) + switch document->body { + | Some(body) => body->appendChild(containerElement)->ignore + | None => () + } container := Some(containerElement) } let cleanupContainer = (container: ref>, ()) => { - open Belt - - let _ = container.contents->Option.map(remove) + switch container.contents { + | Some(contents) => remove(contents) + | None => () + } container := None } -let getContainer = container => { - open Belt - container.contents->Option.getExn -} +let getContainer = container => + switch container.contents { + | Some(contents) => contents + | None => raise(Not_found) + } diff --git a/tests/tests/src/reactTestUtils.resi b/tests/dependencies/rescript-react/src/ReactTestUtils.resi similarity index 98% rename from tests/tests/src/reactTestUtils.resi rename to tests/dependencies/rescript-react/src/ReactTestUtils.resi index 3e9b1d81fc..79afd696e7 100644 --- a/tests/tests/src/reactTestUtils.resi +++ b/tests/dependencies/rescript-react/src/ReactTestUtils.resi @@ -6,7 +6,7 @@ let actAsync: (unit => Js.Promise.t<'a>) => Js.Promise.t external isElement: 'element => bool = "isElement" @module("react-dom/test-utils") -external isElementOfType: ('element, React.component<'props>) => bool = "isElement" +external isElementOfType: ('element, React.component<'props>) => bool = "isElementOfType" @module("react-dom/test-utils") external isDOMComponent: 'element => bool = "isDOMComponent" diff --git a/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.res b/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.res new file mode 100644 index 0000000000..cb00d25bc2 --- /dev/null +++ b/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.res @@ -0,0 +1,44 @@ +/*** + * Important note on this module: + * As soon as React provides a mechanism for error-catching using functional component, + * this is likely to be deprecated and/or move to user space. + */ +type info = {componentStack: string} + +type params<'error> = { + error: 'error, + info: info, +} + +type reactComponentClass +@module("react") external component: reactComponentClass = "Component" +let noOp: reactComponentClass => unit = %raw(`function (_x) {}`) +let reactComponentClass = component +// this is so that the compiler doesn't optimize away the previous line +noOp(reactComponentClass) + +%%raw(` + +var ErrorBoundary = (function (Component) { + function ErrorBoundary(props) { + Component.call(this); + this.state = { error: undefined }; + } + ErrorBoundary.prototype = Object.create(Component.prototype); + ErrorBoundary.prototype.componentDidCatch = function (error, info) { + this.setState({ error: { error: error, info: info } }); + }; + ErrorBoundary.prototype.render = function () { + return this.state.error != undefined + ? this.props.fallback(this.state.error) + : this.props.children; + }; + return ErrorBoundary; +})(reactComponentClass); +`) + +@react.component @val +external make: ( + ~children: React.element, + ~fallback: params<'error> => React.element, +) => React.element = "ErrorBoundary" diff --git a/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.resi b/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.resi new file mode 100644 index 0000000000..0ebdbc8aff --- /dev/null +++ b/tests/dependencies/rescript-react/src/RescriptReactErrorBoundary.resi @@ -0,0 +1,14 @@ +/*** + * Important note on this module: + * As soon as React provides a mechanism for error-catching using functional component, + * this is likely to be deprecated and/or move to user space. + */ +type info = {componentStack: string} + +type params<'error> = { + error: 'error, + info: info, +} + +@react.component +let make: (~children: React.element, ~fallback: params<'error> => React.element) => React.element diff --git a/tests/tests/src/reasonReactRouter.res b/tests/dependencies/rescript-react/src/RescriptReactRouter.res similarity index 61% rename from tests/tests/src/reasonReactRouter.res rename to tests/dependencies/rescript-react/src/RescriptReactRouter.res index 295ab3d413..80f3d758d8 100644 --- a/tests/tests/src/reasonReactRouter.res +++ b/tests/dependencies/rescript-react/src/RescriptReactRouter.res @@ -1,8 +1,14 @@ +@scope("globalThis") +external window: option = "window" + +@scope("globalThis") +external history: option = "history" + @get external location: Dom.window => Dom.location = "location" +/* actually the cb is Dom.event => unit, but let's restrict the access for now */ @send -external /* actually the cb is Dom.event => unit, but let's restrict the access for now */ -addEventListener: (Dom.window, string, unit => unit) => unit = "addEventListener" +external addEventListener: (Dom.window, string, unit => unit) => unit = "addEventListener" @send external removeEventListener: (Dom.window, string, unit => unit) => unit = "removeEventListener" @@ -33,12 +39,6 @@ external createEventNonIEBrowsers: string => Dom.event = "createEvent" @send external initEventNonIEBrowsers: (Dom.event, string, bool, bool) => unit = "initEvent" -@val @scope("globalThis") -external window: option = "window" - -@val @scope("globalThis") -external history: option = "history" - let safeMakeEvent = eventName => if Js.typeof(event) == "function" { makeEventIE11Compatible(eventName) @@ -48,17 +48,16 @@ let safeMakeEvent = eventName => event } -/* This is copied from array.ml. We want to cut dependencies for ReasonReact so +/* This is copied from array.ml. We want to cut dependencies for rescript-react so that it's friendlier to use in size-constrained codebases */ let arrayToList = a => { - open Belt let rec tolist = (i, res) => if i < 0 { res } else { - tolist(i - 1, list{Array.getUnsafe(a, i), ...res}) + tolist(i - 1, list{a->Js.Array2.unsafe_get(i), ...res}) } - tolist(Array.length(a) - 1, list{}) + tolist(a->Js.Array2.length - 1, list{}) } /* if we ever roll our own parser in the future, make sure you test all url combinations e.g. foo.com/?#bar @@ -66,71 +65,88 @@ let arrayToList = a => { /* sigh URLSearchParams doesn't work on IE11, edge16, etc. */ /* actually you know what, not gonna provide search for now. It's a mess. We'll let users roll their own solution/data structure for now */ -let path = () => - switch window { - | None => list{} - | Some(window) => - switch pathname(location(window)) { - | "" - | "/" => - list{} - | raw => - /* remove the preceeding /, which every pathname seems to have */ - let raw = Js.String.sliceToEnd(~from=1, raw) - /* remove the trailing /, which some pathnames might have. Ugh */ - let raw = switch Js.String.get(raw, Js.String.length(raw) - 1) { - | "/" => Js.String.slice(~from=0, ~to_=-1, raw) - | _ => raw - } - arrayToList(Js.String.split("/", raw)) +let pathParse = str => + switch str { + | "" + | "/" => + list{} + | raw => + /* remove the preceeding /, which every pathname seems to have */ + let raw = raw->Js.String2.sliceToEnd(~from=1) + /* remove the trailing /, which some pathnames might have. Ugh */ + let raw = switch raw->Js.String2.get(raw->Js.String2.length - 1) { + | "/" => raw->Js.String2.slice(~from=0, ~to_=-1) + | _ => raw } + /* remove search portion if present in string */ + let raw = switch raw->Js.String2.splitAtMost("?", ~limit=2) { + | [path, _] => path + | _ => raw + } + + raw->Js.String2.split("/")->Js.Array2.filter(item => item->Js.String2.length != 0)->arrayToList + } +let path = (~serverUrlString=?, ()) => + switch (serverUrlString, window) { + | (None, None) => list{} + | (Some(serverUrlString), _) => pathParse(serverUrlString) + | (_, Some(window: Dom.window)) => pathParse(window->location->pathname) } let hash = () => switch window { | None => "" - | Some(window) => - switch hash(location(window)) { + | Some(window: Dom.window) => + switch window->location->hash { | "" | "#" => "" | raw => /* remove the preceeding #, which every hash seems to have. Why is this even included in location.hash?? */ - Js.String.sliceToEnd(~from=1, raw) + raw->Js.String2.sliceToEnd(~from=1) } } -let search = () => - switch window { - | None => "" - | Some(window) => - switch search(location(window)) { - | "" - | "?" => "" - | raw => - /* remove the preceeding ?, which every search seems to have. */ - Js.String.sliceToEnd(~from=1, raw) +let searchParse = str => + switch str { + | "" + | "?" => "" + | raw => + switch raw->Js.String2.splitAtMost("?", ~limit=2) { + | [_, search] => search + | _ => "" } } + +let search = (~serverUrlString=?, ()) => + switch (serverUrlString, window) { + | (None, None) => "" + | (Some(serverUrlString), _) => searchParse(serverUrlString) + | (_, Some(window: Dom.window)) => searchParse(window->location->search) + } + let push = path => switch (history, window) { | (None, _) | (_, None) => () - | (Some(history), Some(window)) => + | (Some(history: Dom.history), Some(window: Dom.window)) => pushState(history, ~href=path) dispatchEvent(window, safeMakeEvent("popstate")) } + let replace = path => switch (history, window) { | (None, _) | (_, None) => () - | (Some(history), Some(window)) => + | (Some(history: Dom.history), Some(window: Dom.window)) => replaceState(history, ~href=path) dispatchEvent(window, safeMakeEvent("popstate")) } + type url = { path: list, hash: string, search: string, } + let urlNotEqual = (a, b) => { let rec listNotEqual = (aList, bList) => switch (aList, bList) { @@ -146,22 +162,31 @@ let urlNotEqual = (a, b) => { } a.hash !== b.hash || (a.search !== b.search || listNotEqual(a.path, b.path)) } + type watcherID = unit => unit -let url = () => {path: path(), hash: hash(), search: search()} + +let url = (~serverUrlString=?, ()) => { + path: path(~serverUrlString?, ()), + hash: hash(), + search: search(~serverUrlString?, ()), +} + /* alias exposed publicly */ let dangerouslyGetInitialUrl = url + let watchUrl = callback => switch window { | None => () => () - | Some(window) => + | Some(window: Dom.window) => let watcherID = () => callback(url()) addEventListener(window, "popstate", watcherID) watcherID } + let unwatchUrl = watcherID => switch window { | None => () - | Some(window) => removeEventListener(window, "popstate", watcherID) + | Some(window: Dom.window) => removeEventListener(window, "popstate", watcherID) } let useUrl = (~serverUrl=?, ()) => { @@ -172,20 +197,18 @@ let useUrl = (~serverUrl=?, ()) => { } ) - React.useEffect0(() => { + React.useEffect(() => { let watcherId = watchUrl(url => setUrl(_ => url)) - /* - * check for updates that may have occured between - * the initial state and the subscribe above - */ + // check for updates that may have occured between the initial state and + // the subscribe above let newUrl = dangerouslyGetInitialUrl() if urlNotEqual(newUrl, url) { setUrl(_ => newUrl) } Some(() => unwatchUrl(watcherId)) - }) + }, []) url } diff --git a/tests/tests/src/reasonReactRouter.resi b/tests/dependencies/rescript-react/src/RescriptReactRouter.resi similarity index 67% rename from tests/tests/src/reasonReactRouter.resi rename to tests/dependencies/rescript-react/src/RescriptReactRouter.resi index 65c9baed35..25357f3267 100644 --- a/tests/tests/src/reasonReactRouter.resi +++ b/tests/dependencies/rescript-react/src/RescriptReactRouter.resi @@ -1,5 +1,6 @@ /** update the url with the string path. Example: `push(\"/book/1\")`, `push(\"/books#title\")` */ let push: string => unit + /** update the url with the string path. modifies the current history entry instead of creating a new one. Example: `replace(\"/book/1\")`, `replace(\"/books#title\")` */ let replace: string => unit type watcherID @@ -11,22 +12,28 @@ type url = { /* the url's query params, if any. The ? symbol is stripped out for you */ search: string, } + /** start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the url record */ let watchUrl: (url => unit) => watcherID + /** stop watching for URL changes */ let unwatchUrl: watcherID => unit -/** this is marked as \"dangerous\" because you technically shouldn't be accessing the URL outside of watchUrl's callback; - you'd read a potentially stale url, instead of the fresh one inside watchUrl. - But this helper is sometimes needed, if you'd like to initialize a page whose display/state depends on the URL, - instead of reading from it in watchUrl's callback, which you'd probably have put inside didMount (aka too late, - the page's already rendered). +/** this is marked as \"dangerous\" because you technically shouldn't + be accessing the URL outside of watchUrl's callback; you'd read a potentially + stale url, instead of the fresh one inside watchUrl. + + But this helper is sometimes needed, if you'd like to initialize a page + whose display/state depends on the URL, instead of reading from it in + watchUrl's callback, which you'd probably have put inside didMount (aka + too late, the page's already rendered). + + So, the correct (and idiomatic) usage of this helper is to only use it in + a component that's also subscribed to watchUrl. Please see + https://github.com/reasonml-community/reason-react-example/blob/master/src/todomvc/TodoItem.re + for an example. */ +let dangerouslyGetInitialUrl: (~serverUrlString: string=?, unit) => url - So, the correct (and idiomatic) usage of this helper is to only use it in a component that's also subscribed to - watchUrl. Please see https://github.com/reasonml-community/reason-react-example/blob/master/src/todomvc/TodoItem.re - for an example. - */ -let dangerouslyGetInitialUrl: unit => url /** hook for watching url changes. * serverUrl is used for ssr. it allows you to specify the url without relying on browser apis existing/working as expected */ diff --git a/tests/gentype_tests/typescript-react-example/package.json b/tests/gentype_tests/typescript-react-example/package.json index 721b4bf4ba..f752a1ba72 100644 --- a/tests/gentype_tests/typescript-react-example/package.json +++ b/tests/gentype_tests/typescript-react-example/package.json @@ -9,7 +9,7 @@ "check": "biome check --changed --no-errors-on-unmatched ." }, "dependencies": { - "@rescript/react": "^0.13.1", + "@rescript/react": "workspace:^", "react": "^18.3.1", "react-dom": "^18.3.1", "rescript": "workspace:^" diff --git a/tests/gentype_tests/typescript-react-example/src/Hooks.res b/tests/gentype_tests/typescript-react-example/src/Hooks.res index 5468269e3a..a324e9d015 100644 --- a/tests/gentype_tests/typescript-react-example/src/Hooks.res +++ b/tests/gentype_tests/typescript-react-example/src/Hooks.res @@ -103,7 +103,7 @@ module ForwardRef = { @genType type testReactContext = React.Context.t -@genType type testReactRef = React.Ref.t +@genType type testReactRef = React.ref @genType type testDomRef = ReactDOM.domRef diff --git a/tests/syntax_tests/data/idempotency/reason-react/src/ReactDOMRe.res b/tests/syntax_tests/data/idempotency/reason-react/src/ReactDOMRe.res index 49b21613c0..2c4bd1d936 100644 --- a/tests/syntax_tests/data/idempotency/reason-react/src/ReactDOMRe.res +++ b/tests/syntax_tests/data/idempotency/reason-react/src/ReactDOMRe.res @@ -81,7 +81,7 @@ type domRef module Ref = { type t = domRef - type currentDomRef = React.Ref.t> + type currentDomRef = React.ref> type callbackDomRef = Js.nullable => unit external domRef: currentDomRef => domRef = "%identity" diff --git a/tests/tests/package.json b/tests/tests/package.json new file mode 100644 index 0000000000..a767eb7a93 --- /dev/null +++ b/tests/tests/package.json @@ -0,0 +1,9 @@ +{ + "name": "@tests/tests", + "private": true, + "dependencies": { + "@rescript/react": "workspace:^", + "rescript": "workspace:^" + }, + "type": "module" +} diff --git a/tests/tests/rescript.json b/tests/tests/rescript.json index 4a21d34945..b5c2a25404 100644 --- a/tests/tests/rescript.json +++ b/tests/tests/rescript.json @@ -11,6 +11,7 @@ "in-source": true, "suffix": ".mjs" }, + "bs-dependencies": ["@rescript/react"], "bsc-flags": [ "-w -3-6-26-27-29-30-32..40-44-45-52-60-9-106+104", "-warn-error A" diff --git a/tests/tests/src/react.mjs b/tests/tests/src/react.mjs deleted file mode 100644 index 1d5280226e..0000000000 --- a/tests/tests/src/react.mjs +++ /dev/null @@ -1,32 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as React from "react"; - -let Children = {}; - -let Context = {}; - -let Fragment = {}; - -let StrictMode = {}; - -let Suspense = {}; - -function lazy_(load) { - return React.lazy(async () => ({ - default: await load() - })); -} - -let Uncurried = {}; - -export { - Children, - Context, - Fragment, - StrictMode, - Suspense, - lazy_, - Uncurried, -} -/* react Not a pure module */ diff --git a/tests/tests/src/react.res b/tests/tests/src/react.res deleted file mode 100644 index f1f6831518..0000000000 --- a/tests/tests/src/react.res +++ /dev/null @@ -1,443 +0,0 @@ -type element = Jsx.element - -@val external null: element = "null" - -external float: float => element = "%identity" -external int: int => element = "%identity" -external string: string => element = "%identity" -external promise: promise => element = "%identity" - -external array: array => element = "%identity" - -type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> - -type component<'props> = Jsx.component<'props> - -external component: componentLike<'props, element> => component<'props> = "%identity" - -@module("react") -external createElement: (component<'props>, 'props) => element = "createElement" - -@module("react") -external cloneElement: (element, 'props) => element = "cloneElement" - -@module("react") -external isValidElement: 'a => bool = "isValidElement" - -@variadic @module("react") -external createElementVariadic: (component<'props>, 'props, array) => element = - "createElement" - -@module("react/jsx-runtime") -external jsx: (component<'props>, 'props) => element = "jsx" - -@module("react/jsx-runtime") -external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx" - -@module("react/jsx-runtime") -external jsxs: (component<'props>, 'props) => element = "jsxs" - -@module("react/jsx-runtime") -external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs" - -type fragmentProps = {children?: element} - -@module("react/jsx-runtime") external jsxFragment: component = "Fragment" - -type ref<'value> = {mutable current: 'value} - -@module("react") -external createRef: unit => ref> = "createRef" - -module Children = { - @module("react") @scope("Children") - external map: (element, element => element) => element = "map" - @module("react") @scope("Children") - external mapWithIndex: (element, (element, int) => element) => element = "map" - @module("react") @scope("Children") - external forEach: (element, element => unit) => unit = "forEach" - @module("react") @scope("Children") - external forEachWithIndex: (element, (element, int) => unit) => unit = "forEach" - @module("react") @scope("Children") - external count: element => int = "count" - @module("react") @scope("Children") - external only: element => element = "only" - @module("react") @scope("Children") - external toArray: element => array = "toArray" -} - -module Context = { - type t<'context> - - type props<'context> = { - value: 'context, - children: element, - } - - @get - external provider: t<'context> => component> = "Provider" -} - -@module("react") -external createContext: 'a => Context.t<'a> = "createContext" - -@module("react") -external forwardRef: (('props, Js.Nullable.t>) => element) => component<'props> = - "forwardRef" - -@module("react") -external memo: component<'props> => component<'props> = "memo" - -@module("react") -external memoCustomCompareProps: ( - component<'props>, - ('props, 'props) => bool, -) => component<'props> = "memo" - -@module("react") external fragment: component = "Fragment" - -module Fragment = { - type props = {key?: string, children: element} - - @module("react") - external make: component = "Fragment" -} - -module StrictMode = { - type props = {key?: string, children: element} - - @module("react") - external make: component = "StrictMode" -} - -module Suspense = { - type props = {key?: string, children?: element, fallback?: element} - - @module("react") - external make: component = "Suspense" -} - -type dynamicallyImportedModule<'a> = {default: component<'a>} - -@module("react") -external lazy_: (unit => promise>) => component<'a> = "lazy" - -let lazy_ = load => lazy_(async () => {default: await load()}) - -/* HOOKS */ - -/* - * Yeah, we know this api isn't great. tl;dr: useReducer instead. - * It's because useState can take functions or non-function values and treats - * them differently. Lazy initializer + callback which returns state is the - * only way to safely have any type of state and be able to update it correctly. - */ -@module("react") -external useState: (unit => 'state) => ('state, ('state => 'state) => unit) = "useState" - -@module("react") -external useReducer: (('state, 'action) => 'state, 'state) => ('state, 'action => unit) = - "useReducer" - -@module("react") -external useReducerWithMapState: ( - ('state, 'action) => 'state, - 'initialState, - 'initialState => 'state, -) => ('state, 'action => unit) = "useReducer" - -@module("react") -external useEffectOnEveryRender: (unit => option unit>) => unit = "useEffect" -@module("react") -external useEffect: (unit => option unit>, 'deps) => unit = "useEffect" -@module("react") -external useEffect0: (unit => option unit>, @as(json`[]`) _) => unit = "useEffect" -@module("react") -external useEffect1: (unit => option unit>, array<'a>) => unit = "useEffect" -@module("react") -external useEffect2: (unit => option unit>, ('a, 'b)) => unit = "useEffect" -@module("react") -external useEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = "useEffect" -@module("react") -external useEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = "useEffect" -@module("react") -external useEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = "useEffect" -@module("react") -external useEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = "useEffect" -@module("react") -external useEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = - "useEffect" - -@module("react") -external useLayoutEffectOnEveryRender: (unit => option unit>) => unit = "useLayoutEffect" -@module("react") -external useLayoutEffect: (unit => option unit>, 'deps) => unit = "useLayoutEffect" -@module("react") -external useLayoutEffect0: (unit => option unit>, @as(json`[]`) _) => unit = - "useLayoutEffect" -@module("react") -external useLayoutEffect1: (unit => option unit>, array<'a>) => unit = "useLayoutEffect" -@module("react") -external useLayoutEffect2: (unit => option unit>, ('a, 'b)) => unit = "useLayoutEffect" -@module("react") -external useLayoutEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = "useLayoutEffect" -@module("react") -external useLayoutEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = - "useLayoutEffect" -@module("react") -external useLayoutEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = - "useLayoutEffect" -@module("react") -external useLayoutEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = - "useLayoutEffect" -@module("react") -external useLayoutEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = - "useLayoutEffect" - -@module("react") -external useMemo: (unit => 'any, 'deps) => 'any = "useMemo" - -@module("react") -external useMemo0: (unit => 'any, @as(json`[]`) _) => 'any = "useMemo" - -@module("react") -external useMemo1: (unit => 'any, array<'a>) => 'any = "useMemo" - -@module("react") -external useMemo2: (unit => 'any, ('a, 'b)) => 'any = "useMemo" - -@module("react") -external useMemo3: (unit => 'any, ('a, 'b, 'c)) => 'any = "useMemo" - -@module("react") -external useMemo4: (unit => 'any, ('a, 'b, 'c, 'd)) => 'any = "useMemo" - -@module("react") -external useMemo5: (unit => 'any, ('a, 'b, 'c, 'd, 'e)) => 'any = "useMemo" - -@module("react") -external useMemo6: (unit => 'any, ('a, 'b, 'c, 'd, 'e, 'f)) => 'any = "useMemo" - -@module("react") -external useMemo7: (unit => 'any, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'any = "useMemo" - -@module("react") -external useCallback: ('f, 'deps) => 'f = "useCallback" - -@module("react") -external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" - -@module("react") -external useCallback1: ('f, array<'a>) => 'f = "useCallback" - -@module("react") -external useCallback2: ('f, ('a, 'b)) => 'f = "useCallback" - -@module("react") -external useCallback3: ('f, ('a, 'b, 'c)) => 'f = "useCallback" - -@module("react") -external useCallback4: ('f, ('a, 'b, 'c, 'd)) => 'f = "useCallback" - -@module("react") -external useCallback5: ('f, ('a, 'b, 'c, 'd, 'e)) => 'f = "useCallback" - -@module("react") -external useCallback6: ('callback, ('a, 'b, 'c, 'd, 'e, 'f)) => 'callback = "useCallback" - -@module("react") -external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = "useCallback" - -@module("react") -external useContext: Context.t<'any> => 'any = "useContext" - -@module("react") -external usePromise: promise<'a> => 'a = "use" - -@module("react") external useRef: 'value => ref<'value> = "useRef" - -@module("react") -external useImperativeHandleOnEveryRender: (Js.Nullable.t>, unit => 'value) => unit = - "useImperativeHandle" - -@module("react") -external useImperativeHandle: (Js.Nullable.t>, unit => 'value, 'deps) => unit = - "useImperativeHandle" - -@module("react") -external useImperativeHandle0: ( - Js.Nullable.t>, - unit => 'value, - @as(json`[]`) _, -) => unit = "useImperativeHandle" - -@module("react") -external useImperativeHandle1: (Js.Nullable.t>, unit => 'value, array<'a>) => unit = - "useImperativeHandle" - -@module("react") -external useImperativeHandle2: (Js.Nullable.t>, unit => 'value, ('a, 'b)) => unit = - "useImperativeHandle" - -@module("react") -external useImperativeHandle3: (Js.Nullable.t>, unit => 'value, ('a, 'b, 'c)) => unit = - "useImperativeHandle" - -@module("react") -external useImperativeHandle4: ( - Js.Nullable.t>, - unit => 'value, - ('a, 'b, 'c, 'd), -) => unit = "useImperativeHandle" - -@module("react") -external useImperativeHandle5: ( - Js.Nullable.t>, - unit => 'value, - ('a, 'b, 'c, 'd, 'e), -) => unit = "useImperativeHandle" - -@module("react") -external useImperativeHandle6: ( - Js.Nullable.t>, - unit => 'value, - ('a, 'b, 'c, 'd, 'e, 'f), -) => unit = "useImperativeHandle" - -@module("react") -external useImperativeHandle7: ( - Js.Nullable.t>, - unit => 'value, - ('a, 'b, 'c, 'd, 'e, 'f, 'g), -) => unit = "useImperativeHandle" - -@module("react") external useId: unit => string = "useId" - -/** `useDeferredValue` is a React Hook that lets you defer updating a part of the UI. */ -@module("react") -external useDeferredValue: ('value, ~initialValue: 'value=?) => 'value = "useDeferredValue" - -@module("react") -external useInsertionEffectOnEveryRender: (unit => option unit>) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect: (unit => option unit>, 'deps) => unit = "useInsertionEffect" -@module("react") -external useInsertionEffect0: (unit => option unit>, @as(json`[]`) _) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect1: (unit => option unit>, array<'a>) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect2: (unit => option unit>, ('a, 'b)) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = - "useInsertionEffect" -@module("react") -external useInsertionEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = - "useInsertionEffect" - -@module("react") -external useSyncExternalStore: ( - ~subscribe: (unit => unit) => unit => unit, - ~getSnapshot: unit => 'state, -) => 'state = "useSyncExternalStore" - -@module("react") -external useSyncExternalStoreWithServerSnapshot: ( - ~subscribe: (unit => unit) => unit => unit, - ~getSnapshot: unit => 'state, - ~getServerSnapshot: unit => 'state, -) => 'state = "useSyncExternalStore" - -module Uncurried = { - @module("react") - external useState: (unit => 'state) => ('state, ('state => 'state) => unit) = "useState" - - @module("react") - external useReducer: (('state, 'action) => 'state, 'state) => ('state, 'action => unit) = - "useReducer" - - @module("react") - external useReducerWithMapState: ( - ('state, 'action) => 'state, - 'initialState, - 'initialState => 'state, - ) => ('state, 'action => unit) = "useReducer" - - @module("react") - external useCallback: ('f, 'deps) => 'f = "useCallback" - - @module("react") - external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" - - @module("react") - external useCallback1: ('f, array<'a>) => 'f = "useCallback" - - @module("react") - external useCallback2: ('f, ('a, 'b)) => 'f = "useCallback" - - @module("react") - external useCallback3: ('f, ('a, 'b, 'c)) => 'f = "useCallback" - - @module("react") - external useCallback4: ('f, ('a, 'b, 'c, 'd)) => 'f = "useCallback" - - @module("react") - external useCallback5: ('f, ('a, 'b, 'c, 'd, 'e)) => 'f = "useCallback" - - @module("react") - external useCallback6: ('callback, ('a, 'b, 'c, 'd, 'e, 'f)) => 'callback = "useCallback" - - @module("react") - external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = "useCallback" -} - -@set -external setDisplayName: (component<'props>, string) => unit = "displayName" - -@get @return(nullable) -external displayName: component<'props> => option = "displayName" - -// Actions - -type transitionFunction = unit => promise - -type transitionStartFunction = transitionFunction => unit - -/** `useTransition` is a React Hook that lets you render a part of the UI in the background. */ -@module("react") -external useTransition: unit => (bool, transitionStartFunction) = "useTransition" - -type action<'state, 'payload> = ('state, 'payload) => promise<'state> - -type formAction<'formData> = 'formData => promise - -/** `useActionState` is a Hook that allows you to update state based on the result of a form action. */ -@module("react") -external useActionState: ( - action<'state, 'payload>, - 'state, - ~permalink: string=?, -) => ('state, formAction<'payload>, bool) = "useActionState" - -/** `useOptimistic` is a React Hook that lets you optimistically update the UI. */ -@module("react") -external useOptimistic: ( - 'state, - ~updateFn: ('state, 'action) => 'state=?, -) => ('state, 'action => unit) = "useOptimistic" - -/** `act` is a test helper to apply pending React updates before making assertions. */ -@module("react") -external act: (unit => promise) => promise = "act" diff --git a/tests/tests/src/reactDOM.mjs b/tests/tests/src/reactDOM.mjs deleted file mode 100644 index 3064f39b11..0000000000 --- a/tests/tests/src/reactDOM.mjs +++ /dev/null @@ -1,59 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Primitive_option from "rescript/lib/es6/Primitive_option.js"; - -let Root = {}; - -let Client = { - Root: Root -}; - -function getString(formData, name) { - let value = formData.get(name); - if (!(value == null) && typeof value === "string") { - return Primitive_option.some(value); - } - -} - -function getFile(formData, name) { - let value = formData.get(name); - if (!(value == null) && typeof value !== "string") { - return Primitive_option.some(value); - } - -} - -function getAll(t, string) { - return t.getAll(string).map(value => { - if (typeof value === "string") { - return { - TAG: "String", - _0: value - }; - } else { - return { - TAG: "File", - _0: value - }; - } - }); -} - -let FormData = { - getString: getString, - getFile: getFile, - getAll: getAll -}; - -let Ref = {}; - -let Style; - -export { - Client, - FormData, - Ref, - Style, -} -/* No side effect */ diff --git a/tests/tests/src/reactDOM.res b/tests/tests/src/reactDOM.res deleted file mode 100644 index 3cb44aac83..0000000000 --- a/tests/tests/src/reactDOM.res +++ /dev/null @@ -1,249 +0,0 @@ -/* First time reading a ReScript file? */ -/* `external` is the foreign function call in OCaml. */ -/* here we're saying `I guarantee that on the JS side, we have a `render` function in the module "react-dom" - that takes in a reactElement, a dom element, and returns unit (nothing) */ -/* It's like `let`, except you're pointing the implementation to the JS side. The compiler will inline these - calls and add the appropriate `require("react-dom")` in the file calling this `render` */ - -// Helper so that ReactDOM itself doesn't bring any runtime -@val @return(nullable) -external querySelector: string => option = "document.querySelector" - -module Client = { - module Root = { - type t - - @send external render: (t, React.element) => unit = "render" - - @send external unmount: (t, unit) => unit = "unmount" - } - - @module("react-dom/client") - external createRoot: Dom.element => Root.t = "createRoot" - - @module("react-dom/client") - external hydrateRoot: (Dom.element, React.element) => Root.t = "hydrateRoot" -} - -// Very rudimentary form data bindings -module FormData = { - type t - type file - - type formValue = - | String(string) - | File(file) - - @new external make: unit => t = "FormData" - - @send external append: (t, string, ~filename: string=?) => unit = "append" - @send external delete: (t, string) => unit = "delete" - @return(nullable) @send external getUnsafe: (t, string) => option<'a> = "get" - @send external getAllUnsafe: (t, string) => array<'a> = "getAll" - - let getString = (formData, name) => { - switch formData->getUnsafe(name) { - | Some(value) => Js.typeof(value) === "string" ? Some(value) : None - | _ => None - } - } - - external _asFile: 'a => file = "%identity" - - let getFile = (formData, name) => { - switch formData->getUnsafe(name) { - | Some(value) => Js.typeof(value) === "string" ? None : Some(value->_asFile) - | _ => None - } - } - - let getAll = (t, string) => { - t - ->getAllUnsafe(string) - ->Js.Array2.map(value => { - Js.typeof(value) === "string" ? String(value) : File(value->_asFile) - }) - } - - @send external set: (string, string) => unit = "set" - @send external has: string => bool = "has" - // @send external keys: t => Iterator.t = "keys"; - // @send external values: t => Iterator.t = "values"; -} - -@module("react-dom") -external createPortal: (React.element, Dom.element) => React.element = "createPortal" - -external domElementToObj: Dom.element => {..} = "%identity" - -type style = JsxDOMStyle.t - -type domRef = JsxDOM.domRef - -module Ref = { - type t = domRef - type currentDomRef = React.ref> - type callbackDomRef = Js.nullable => option unit> - - external domRef: currentDomRef => domRef = "%identity" - external callbackDomRef: callbackDomRef => domRef = "%identity" -} - -// Hooks - -type formStatus<'state> = { - /** If true, this means the parent is pending submission. Otherwise, false. */ - pending: bool, - /** An object implementing the FormData interface that contains the data the parent is submitting. If there is no active submission or no parent , it will be null. */ - data: FormData.t, - /** This represents whether the parent is submitting with either a GET or POST HTTP method. By default, a will use the GET method and can be specified by the method property. */ - method: [#get | #post], - /** A reference to the function passed to the action prop on the parent . If there is no parent , the property is null. If there is a URI value provided to the action prop, or no action prop specified, status.action will be null. */ - action: React.action<'state, FormData.t>, -} - -external formAction: React.formAction => string = "%identity" - -/** `useFormStatus` is a Hook that gives you status information of the last form submission. */ -@module("react-dom") -external useFormStatus: unit => formStatus<'state> = "useFormStatus" - -// Resource Preloading APIs - -/** The CORS policy to use. */ -type crossOrigin = [ - | #anonymous - | #"use-credentials" -] - -/** The Referrer header to send when fetching. */ -type referrerPolicy = [ - | #"referrer-when-downgrade" - | #"no-referrer" - | #origin - | #"origin-when-cross-origin" - | #"unsafe-url" -] - -/** Suggests a relative priority for fetching the resource. */ -type fetchPriority = [#auto | #high | #low] - -/** `prefetchDNS` lets you eagerly look up the IP of a server that you expect to load resources from. */ -@module("react-dom") -external prefetchDNS: string => unit = "prefetchDNS" - -/** `preconnect` lets you eagerly connect to a server that you expect to load resources from. */ -@module("react-dom") -external preconnect: string => unit = "preconnect" - -type preloadOptions = { - /** The type of resource. */ - @as("as") - as_: [ - | #audio - | #document - | #embed - | #fetch - | #font - | #image - | #object - | #script - | #style - | #track - | #video - | #worker - ], - /** The CORS policy to use. It is required when as is set to "fetch". */ - crossOrigin?: crossOrigin, - /** The Referrer header to send when fetching. */ - referrerPolicy?: referrerPolicy, - /** A cryptographic hash of the resource, to verify its authenticity. */ - integrity?: string, - /** The MIME type of the resource. */ - @as("type") - type_?: string, - /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ - nonce?: string, - /** Suggests a relative priority for fetching the resource. */ - fetchPriority?: fetchPriority, - /** For use only with as: "image". Specifies the source set of the image. */ - imageSrcSet?: string, - /** For use only with as: "image". Specifies the sizes of the image. */ - imageSizes?: string, -} - -/** `preload` lets you eagerly fetch a resource such as a stylesheet, font, or external script that you expect to use. */ -@module("react-dom") -external preload: (string, preloadOptions) => unit = "preload" - -type preloadModuleOptions = { - /** The type of resource. */ - @as("as") - as_: [#script], - /** The CORS policy to use. It is required when as is set to "fetch". */ - crossOrigin?: crossOrigin, - /** A cryptographic hash of the resource, to verify its authenticity. */ - integrity?: string, - /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ - nonce?: string, -} - -/** `preloadModule` lets you eagerly fetch an ESM module that you expect to use. */ -@module("react-dom") -external preloadModule: (string, preloadModuleOptions) => unit = "preloadModule" - -type preinitOptions = { - /** The type of resource. */ - @as("as") - as_: [#script | #style], - /** Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. */ - precedence?: [#reset | #low | #medium | #high], - /** The CORS policy to use. It is required when as is set to "fetch". */ - crossOrigin?: crossOrigin, - /** The Referrer header to send when fetching. */ - referrerPolicy?: referrerPolicy, - /** A cryptographic hash of the resource, to verify its authenticity. */ - integrity?: string, - nonce?: string, - /** Suggests a relative priority for fetching the resource. */ - fetchPriority?: fetchPriority, -} - -/** `preinit` lets you eagerly fetch and evaluate a stylesheet or external script. */ -@module("react-dom") -external preinit: (string, preinitOptions) => unit = "preinit" - -/** To preinit an ESM module, call the `preinitModule` function from react-dom. */ -@module("react-dom") -external preinitModule: (string, preloadModuleOptions) => unit = "preinitModule" - -// Runtime - -type domProps = JsxDOM.domProps - -@variadic @module("react") -external createElement: (string, ~props: domProps=?, array) => React.element = - "createElement" - -@variadic @module("react") -external createDOMElementVariadic: ( - string, - ~props: domProps=?, - array, -) => React.element = "createElement" - -external someElement: React.element => option = "%identity" - -@module("react/jsx-runtime") -external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx" - -@module("react/jsx-runtime") -external jsxKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element = "jsx" - -@module("react/jsx-runtime") -external jsxs: (string, JsxDOM.domProps) => Jsx.element = "jsxs" - -@module("react/jsx-runtime") -external jsxsKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element = "jsxs" - -module Style = JsxDOMStyle diff --git a/tests/tests/src/reactDOMServer.mjs b/tests/tests/src/reactDOMServer.mjs deleted file mode 100644 index d856702bfe..0000000000 --- a/tests/tests/src/reactDOMServer.mjs +++ /dev/null @@ -1,2 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE -/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */ diff --git a/tests/tests/src/reactEvent.mjs b/tests/tests/src/reactEvent.mjs deleted file mode 100644 index 093c936081..0000000000 --- a/tests/tests/src/reactEvent.mjs +++ /dev/null @@ -1,51 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - - -let Synthetic = {}; - -let Clipboard = {}; - -let Composition = {}; - -let Keyboard = {}; - -let Focus = {}; - -let Form = {}; - -let Mouse = {}; - -let Selection = {}; - -let Touch = {}; - -let UI = {}; - -let Wheel = {}; - -let Media = {}; - -let Image = {}; - -let Animation = {}; - -let Transition = {}; - -export { - Synthetic, - Clipboard, - Composition, - Keyboard, - Focus, - Form, - Mouse, - Selection, - Touch, - UI, - Wheel, - Media, - Image, - Animation, - Transition, -} -/* No side effect */ diff --git a/tests/tests/src/reactEvent.res b/tests/tests/src/reactEvent.res deleted file mode 100644 index 54921be983..0000000000 --- a/tests/tests/src/reactEvent.res +++ /dev/null @@ -1,196 +0,0 @@ -type synthetic<'a> - -module MakeEventWithType = ( - Type: { - type t - }, -) => { - @get external bubbles: Type.t => bool = "bubbles" - @get external cancelable: Type.t => bool = "cancelable" - @get external currentTarget: Type.t => {..} = "currentTarget" /* Should return Dom.eventTarget */ - @get external defaultPrevented: Type.t => bool = "defaultPrevented" - @get external eventPhase: Type.t => int = "eventPhase" - @get external isTrusted: Type.t => bool = "isTrusted" - @get external nativeEvent: Type.t => {..} = "nativeEvent" /* Should return Dom.event */ - @send external preventDefault: Type.t => unit = "preventDefault" - @send - external isDefaultPrevented: Type.t => bool = "isDefaultPrevented" - @send external stopPropagation: Type.t => unit = "stopPropagation" - @send - external isPropagationStopped: Type.t => bool = "isPropagationStopped" - @get external target: Type.t => {..} = "target" /* Should return Dom.eventTarget */ - @get external timeStamp: Type.t => float = "timeStamp" - @get external type_: Type.t => string = "type" - @send external persist: Type.t => unit = "persist" -} - -module Synthetic = { - type tag - type t = synthetic - @get external bubbles: synthetic<'a> => bool = "bubbles" - @get external cancelable: synthetic<'a> => bool = "cancelable" - @get - external currentTarget: synthetic<'a> => {..} = - "currentTarget" /* Should return Dom.eventTarget */ - @get - external defaultPrevented: synthetic<'a> => bool = "defaultPrevented" - @get external eventPhase: synthetic<'a> => int = "eventPhase" - @get external isTrusted: synthetic<'a> => bool = "isTrusted" - @get - external nativeEvent: synthetic<'a> => {..} = "nativeEvent" /* Should return Dom.event */ - @send - external preventDefault: synthetic<'a> => unit = "preventDefault" - @send - external isDefaultPrevented: synthetic<'a> => bool = "isDefaultPrevented" - @send - external stopPropagation: synthetic<'a> => unit = "stopPropagation" - @send - external isPropagationStopped: synthetic<'a> => bool = "isPropagationStopped" - @get external target: synthetic<'a> => {..} = "target" /* Should return Dom.eventTarget */ - @get external timeStamp: synthetic<'a> => float = "timeStamp" - @get external type_: synthetic<'a> => string = "type" - @send external persist: synthetic<'a> => unit = "persist" -} - -/* Cast any event type to the general synthetic type. This is safe, since synthetic is more general */ -external toSyntheticEvent: synthetic<'a> => Synthetic.t = "%identity" - -module Clipboard = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external clipboardData: t => {..} = "clipboardData" /* Should return Dom.dataTransfer */ -} - -module Composition = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external data: t => string = "data" -} - -module Keyboard = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external altKey: t => bool = "altKey" - @get external charCode: t => int = "charCode" - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external key: t => string = "key" - @get external keyCode: t => int = "keyCode" - @get external locale: t => string = "locale" - @get external location: t => int = "location" - @get external metaKey: t => bool = "metaKey" - @get external repeat: t => bool = "repeat" - @get external shiftKey: t => bool = "shiftKey" - @get external which: t => int = "which" -} - -module Focus = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get @return(nullable) - external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ -} - -module Form = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) -} - -module Mouse = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external altKey: t => bool = "altKey" - @get external button: t => int = "button" - @get external buttons: t => int = "buttons" - @get external clientX: t => int = "clientX" - @get external clientY: t => int = "clientY" - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external metaKey: t => bool = "metaKey" - @get external movementX: t => int = "movementX" - @get external movementY: t => int = "movementY" - @get external pageX: t => int = "pageX" - @get external pageY: t => int = "pageY" - @get @return(nullable) - external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ - @get external screenX: t => int = "screenX" - @get external screenY: t => int = "screenY" - @get external shiftKey: t => bool = "shiftKey" -} - -module Selection = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) -} - -module Touch = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external altKey: t => bool = "altKey" - @get external changedTouches: t => {..} = "changedTouches" /* Should return Dom.touchList */ - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external metaKey: t => bool = "metaKey" - @get external shiftKey: t => bool = "shiftKey" - @get external targetTouches: t => {..} = "targetTouches" /* Should return Dom.touchList */ - @get external touches: t => {..} = "touches" /* Should return Dom.touchList */ -} - -module UI = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external detail: t => int = "detail" - @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ -} - -module Wheel = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external deltaMode: t => int = "deltaMode" - @get external deltaX: t => float = "deltaX" - @get external deltaY: t => float = "deltaY" - @get external deltaZ: t => float = "deltaZ" -} - -module Media = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) -} - -module Image = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) -} - -module Animation = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external animationName: t => string = "animationName" - @get external pseudoElement: t => string = "pseudoElement" - @get external elapsedTime: t => float = "elapsedTime" -} - -module Transition = { - type tag - type t = synthetic - include MakeEventWithType({type t = t}) - @get external propertyName: t => string = "propertyName" - @get external pseudoElement: t => string = "pseudoElement" - @get external elapsedTime: t => float = "elapsedTime" -} diff --git a/tests/tests/src/reactEvent.resi b/tests/tests/src/reactEvent.resi deleted file mode 100644 index 44a6535aa8..0000000000 --- a/tests/tests/src/reactEvent.resi +++ /dev/null @@ -1,405 +0,0 @@ -/* This is the whole synthetic event system of ReactJS/ReasonReact. The first module `Synthetic` represents - the generic synthetic event. The rest are the specific ones. - - In each module, the type `t` commonly means "the type of that module" (OCaml convention). In our case, e.g. - `ReactEvent.Mouse.t` represents a ReactJS synthetic mouse event. You'd use it to type your props: - - ``` - type props = { - onClick: ReactEvent.Mouse.t => unit - }; - ``` - - All the methods and properties of a type of event are in the module, as seen below. - - Each module also has a `tag` type. You can ignore it; they're only needed by their `t` type. This way, we - get to allow a base `Synthetic` event module with generic methods. So e.g. even a mouse event (`Mouse.t`) - get to be passed to a generic handler: - - ``` - let handleClick = ({state, props}, event) => { - ReactEvent.Mouse.preventDefault(event); - ... - }; - let handleSubmit = ({state, props}, event) => { - /* this handler can be triggered by either a Keyboard or a Mouse event; conveniently use the generic - preventDefault */ - ReactEvent.Synthetic.preventDefault(event); - ... - }; - - let render = (_) => ; - ``` - - How to translate idioms from ReactJS: - - 1. myMouseEvent.preventDefault() -> ReactEvent.Mouse.preventDefault(myMouseEvent) - 2. myKeyboardEvent.which -> ReactEvent.Keyboard.which(myKeyboardEvent) - */ -type synthetic<'a> - -module Synthetic: { - type tag - type t = synthetic - @get external bubbles: synthetic<'a> => bool = "bubbles" - @get external cancelable: synthetic<'a> => bool = "cancelable" - @get - external currentTarget: synthetic<'a> => {..} = "currentTarget" - @get - external defaultPrevented: synthetic<'a> => bool = "defaultPrevented" - @get external eventPhase: synthetic<'a> => int = "eventPhase" - @get external isTrusted: synthetic<'a> => bool = "isTrusted" - @get - external nativeEvent: synthetic<'a> => {..} = "nativeEvent" - @send - external preventDefault: synthetic<'a> => unit = "preventDefault" - @send - external isDefaultPrevented: synthetic<'a> => bool = "isDefaultPrevented" - @send - external stopPropagation: synthetic<'a> => unit = "stopPropagation" - @send - external isPropagationStopped: synthetic<'a> => bool = "isPropagationStopped" - @get external target: synthetic<'a> => {..} = "target" - @get external timeStamp: synthetic<'a> => float = "timeStamp" - @get external type_: synthetic<'a> => string = "type" - @send external persist: synthetic<'a> => unit = "persist" -} - -/* Cast any event type to the general synthetic type. This is safe, since synthetic is more general */ -external toSyntheticEvent: synthetic<'a> => Synthetic.t = "%identity" - -module Clipboard: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external clipboardData: t => {..} = "clipboardData" /* Should return Dom.dataTransfer */ -} - -module Composition: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external data: t => string = "data" -} - -module Keyboard: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external altKey: t => bool = "altKey" - @get external charCode: t => int = "charCode" - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external key: t => string = "key" - @get external keyCode: t => int = "keyCode" - @get external locale: t => string = "locale" - @get external location: t => int = "location" - @get external metaKey: t => bool = "metaKey" - @get external repeat: t => bool = "repeat" - @get external shiftKey: t => bool = "shiftKey" - @get external which: t => int = "which" -} - -module Focus: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get @return(nullable) - external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ -} - -module Form: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" -} - -module Mouse: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external altKey: t => bool = "altKey" - @get external button: t => int = "button" - @get external buttons: t => int = "buttons" - @get external clientX: t => int = "clientX" - @get external clientY: t => int = "clientY" - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external metaKey: t => bool = "metaKey" - @get external movementX: t => int = "movementX" - @get external movementY: t => int = "movementY" - @get external pageX: t => int = "pageX" - @get external pageY: t => int = "pageY" - @get @return(nullable) - external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ - @get external screenX: t => int = "screenX" - @get external screenY: t => int = "screenY" - @get external shiftKey: t => bool = "shiftKey" -} - -module Selection: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" -} - -module Touch: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external altKey: t => bool = "altKey" - @get external changedTouches: t => {..} = "changedTouches" /* Should return Dom.touchList */ - @get external ctrlKey: t => bool = "ctrlKey" - @send - external getModifierState: (t, string) => bool = "getModifierState" - @get external metaKey: t => bool = "metaKey" - @get external shiftKey: t => bool = "shiftKey" - @get external targetTouches: t => {..} = "targetTouches" /* Should return Dom.touchList */ - @get external touches: t => {..} = "touches" /* Should return Dom.touchList */ -} - -module UI: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external detail: t => int = "detail" - @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ -} - -module Wheel: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external deltaMode: t => int = "deltaMode" - @get external deltaX: t => float = "deltaX" - @get external deltaY: t => float = "deltaY" - @get external deltaZ: t => float = "deltaZ" -} - -module Media: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" -} - -module Image: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" -} - -module Animation: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external animationName: t => string = "animationName" - @get external pseudoElement: t => string = "pseudoElement" - @get external elapsedTime: t => float = "elapsedTime" -} - -module Transition: { - type tag - type t = synthetic - @get external bubbles: t => bool = "bubbles" - @get external cancelable: t => bool = "cancelable" - @get external currentTarget: t => {..} = "currentTarget" - @get external defaultPrevented: t => bool = "defaultPrevented" - @get external eventPhase: t => int = "eventPhase" - @get external isTrusted: t => bool = "isTrusted" - @get external nativeEvent: t => {..} = "nativeEvent" - @send external preventDefault: t => unit = "preventDefault" - @send external isDefaultPrevented: t => bool = "isDefaultPrevented" - @send external stopPropagation: t => unit = "stopPropagation" - @send external isPropagationStopped: t => bool = "isPropagationStopped" - @get external target: t => {..} = "target" - @get external timeStamp: t => float = "timeStamp" - @get external type_: t => string = "type" - @send external persist: t => unit = "persist" - @get external propertyName: t => string = "propertyName" - @get external pseudoElement: t => string = "pseudoElement" - @get external elapsedTime: t => float = "elapsedTime" -} diff --git a/tests/tests/src/reactTestUtils.mjs b/tests/tests/src/reactTestUtils.mjs deleted file mode 100644 index 33c59321af..0000000000 --- a/tests/tests/src/reactTestUtils.mjs +++ /dev/null @@ -1,91 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as Belt_Array from "rescript/lib/es6/Belt_Array.js"; -import * as Belt_Option from "rescript/lib/es6/Belt_Option.js"; -import * as Primitive_option from "rescript/lib/es6/Primitive_option.js"; -import * as TestUtils from "react-dom/test-utils"; - -function act(func) { - let reactFunc = () => { - func(); - }; - TestUtils.act(reactFunc); -} - -function actAsync(func) { - return TestUtils.act(() => func()); -} - -function changeWithValue(element, value) { - let event = { - target: { - value: value - } - }; - TestUtils.Simulate.change(element, event); -} - -function changeWithChecked(element, value) { - let event = { - target: { - checked: value - } - }; - TestUtils.Simulate.change(element, event); -} - -let Simulate = { - changeWithValue: changeWithValue, - changeWithChecked: changeWithChecked -}; - -function findBySelector(element, selector) { - return element.querySelector(selector); -} - -function findByAllSelector(element, selector) { - return Array.from(element.querySelectorAll(selector)); -} - -function findBySelectorAndTextContent(element, selector, content) { - return Belt_Array.getBy(Array.from(element.querySelectorAll(selector)), node => node.textContent === content); -} - -function findBySelectorAndPartialTextContent(element, selector, content) { - return Belt_Array.getBy(Array.from(element.querySelectorAll(selector)), node => node.textContent.includes(content)); -} - -let DOM = { - findBySelector: findBySelector, - findByAllSelector: findByAllSelector, - findBySelectorAndTextContent: findBySelectorAndTextContent, - findBySelectorAndPartialTextContent: findBySelectorAndPartialTextContent -}; - -function prepareContainer(container, param) { - let containerElement = document.createElement("div"); - Belt_Option.map(document.body, body => body.appendChild(containerElement)); - container.contents = Primitive_option.some(containerElement); -} - -function cleanupContainer(container, param) { - Belt_Option.map(container.contents, prim => { - prim.remove(); - }); - container.contents = undefined; -} - -function getContainer(container) { - return Belt_Option.getExn(container.contents); -} - -export { - act, - actAsync, - Simulate, - DOM, - prepareContainer, - cleanupContainer, - getContainer, -} -/* react-dom/test-utils Not a pure module */ diff --git a/tests/tests/src/reasonReact.mjs b/tests/tests/src/reasonReact.mjs deleted file mode 100644 index e1f8ab1ec5..0000000000 --- a/tests/tests/src/reasonReact.mjs +++ /dev/null @@ -1,137 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as React from "react"; -import * as Js_array from "rescript/lib/es6/Js_array.js"; - -function createDomElement(s, props, children) { - let vararg = Js_array.concat(children, [ - s, - props - ]); - return React.createElement.apply(null, vararg); -} - -function anyToUnit(param) { - -} - -function anyToTrue(param) { - return true; -} - -function willReceivePropsDefault(param) { - return param.state; -} - -function renderDefault(_self) { - return "RenderNotImplemented"; -} - -function initialStateDefault() { - -} - -function reducerDefault(_action, _state) { - return "NoUpdate"; -} - -function basicComponent(debugName) { - return { - debugName: debugName, - reactClassInternal: debugName, - handedOffState: { - contents: undefined - }, - willReceiveProps: willReceivePropsDefault, - didMount: anyToUnit, - didUpdate: anyToUnit, - willUnmount: anyToUnit, - willUpdate: anyToUnit, - shouldUpdate: anyToTrue, - render: renderDefault, - initialState: initialStateDefault, - retainedProps: undefined, - reducer: reducerDefault, - jsElementWrapped: undefined - }; -} - -let statelessComponent = basicComponent; - -let statelessComponentWithRetainedProps = basicComponent; - -let reducerComponent = basicComponent; - -let reducerComponentWithRetainedProps = basicComponent; - -function element(keyOpt, refOpt, component) { - let key = keyOpt !== undefined ? keyOpt : undefined; - let ref = refOpt !== undefined ? refOpt : undefined; - let element$1 = { - TAG: "Element", - _0: component - }; - let jsElementWrapped = component.jsElementWrapped; - if (jsElementWrapped !== undefined) { - return jsElementWrapped(key, ref); - } else { - return React.createElement(component.reactClassInternal, { - key: key, - ref: ref, - reasonProps: element$1 - }); - } -} - -function wrapReasonForJs(component, jsPropsToReason) { - let uncurriedJsPropsToReason = jsProps => jsPropsToReason(jsProps); - component.reactClassInternal.prototype.jsPropsToReason = uncurriedJsPropsToReason; - return component.reactClassInternal; -} - -let dummyInteropComponent = basicComponent("interop"); - -function wrapJsForReason(reactClass, props, children) { - let jsElementWrapped = (extra, extra$1) => { - let props$1 = Object.assign(Object.assign({}, props), { - ref: extra$1, - key: extra - }); - let varargs = Js_array.concat(children, [ - reactClass, - props$1 - ]); - return React.createElement.apply(null, varargs); - }; - return { - debugName: dummyInteropComponent.debugName, - reactClassInternal: dummyInteropComponent.reactClassInternal, - handedOffState: dummyInteropComponent.handedOffState, - willReceiveProps: dummyInteropComponent.willReceiveProps, - didMount: dummyInteropComponent.didMount, - didUpdate: dummyInteropComponent.didUpdate, - willUnmount: dummyInteropComponent.willUnmount, - willUpdate: dummyInteropComponent.willUpdate, - shouldUpdate: dummyInteropComponent.shouldUpdate, - render: dummyInteropComponent.render, - initialState: dummyInteropComponent.initialState, - retainedProps: dummyInteropComponent.retainedProps, - reducer: dummyInteropComponent.reducer, - jsElementWrapped: jsElementWrapped - }; -} - -let Router; - -export { - statelessComponent, - statelessComponentWithRetainedProps, - reducerComponent, - reducerComponentWithRetainedProps, - element, - wrapReasonForJs, - createDomElement, - wrapJsForReason, - Router, -} -/* dummyInteropComponent Not a pure module */ diff --git a/tests/tests/src/reasonReact.res b/tests/tests/src/reasonReact.res deleted file mode 100644 index c86c69ef87..0000000000 --- a/tests/tests/src/reasonReact.res +++ /dev/null @@ -1,310 +0,0 @@ -// Prevent warning about the `retainedProps` field being defined in both -// `self` and `componentSpec` record types. -@@warning("-30") - -type reactClass - -type jsProps - -type reactElement = React.element - -type reactRef - -@val external null: reactElement = "null" - -external string: string => reactElement = "%identity" - -external array: array => reactElement = "%identity" - -external refToJsObj: reactRef => {..} = "%identity" - -@variadic @val @module("react") -external createElement: (reactClass, ~props: {..}=?, array) => reactElement = - "createElement" - -@variadic @module("react") -external cloneElement: (reactElement, ~props: {..}=?, array) => reactElement = - "cloneElement" - -@val @module("react") -external createElementVerbatim: 'a = "createElement" - -let createDomElement = (s, ~props, children) => { - let vararg = Js.Array.concat(children, [Obj.magic(s), Obj.magic(props)]) - /* Use varargs to avoid warnings on duplicate keys in children */ - Obj.magic(createElementVerbatim)["apply"](Js.Nullable.null, vararg) -} - -@val external magicNull: 'a = "null" - -type reactClassInternal = reactClass - -type renderNotImplemented = RenderNotImplemented - -type stateless = unit - -type noRetainedProps = unit - -type actionless = unit - -/* ** - * Elements are what JSX blocks become. They represent the *potential* for a - * component instance and state to be created / updated. They are not yet - * instances. - */ -type rec element = Element(component<'state, 'retainedProps, 'action>): element -and jsPropsToReason<'jsProps, 'state, 'retainedProps, 'action> = 'jsProps => component< - 'state, - 'retainedProps, - 'action, -> -and uncurriedJsPropsToReason<'jsProps, 'state, 'retainedProps, 'action> = 'jsProps => component< - 'state, - 'retainedProps, - 'action, -> -/* ** - * Type of hidden field for Reason components that use JS components - */ -and jsElementWrapped = option< - (~key: Js.nullable, ~ref: Js.nullable => unit>) => reactElement, -> -and update<'state, 'retainedProps, 'action> = - | NoUpdate - | Update('state) - | SideEffects(self<'state, 'retainedProps, 'action> => unit) - | UpdateWithSideEffects('state, self<'state, 'retainedProps, 'action> => unit) -/* ** - * Granularly types state, and initial state as being independent, so that we - * may include a template that all instances extend from. - */ -and componentSpec<'state, 'initialState, 'retainedProps, 'initialRetainedProps, 'action> = { - debugName: string, - reactClassInternal: reactClassInternal, - /* Keep here as a way to prove that the API may be implemented soundly */ - mutable handedOffState: ref>, - willReceiveProps: self<'state, 'retainedProps, 'action> => 'state, - didMount: self<'state, 'retainedProps, 'action> => unit, - didUpdate: oldNewSelf<'state, 'retainedProps, 'action> => unit, - willUnmount: self<'state, 'retainedProps, 'action> => unit, - willUpdate: oldNewSelf<'state, 'retainedProps, 'action> => unit, - shouldUpdate: oldNewSelf<'state, 'retainedProps, 'action> => bool, - render: self<'state, 'retainedProps, 'action> => reactElement, - initialState: unit => 'initialState, - retainedProps: 'initialRetainedProps, - reducer: ('action, 'state) => update<'state, 'retainedProps, 'action>, - jsElementWrapped: jsElementWrapped, -} -and component<'state, 'retainedProps, 'action> = componentSpec< - 'state, - 'state, - 'retainedProps, - 'retainedProps, - 'action, -> -and self<'state, 'retainedProps, 'action> = { - handle: 'payload. (('payload, self<'state, 'retainedProps, 'action>) => unit, 'payload) => unit, - state: 'state, - retainedProps: 'retainedProps, - send: 'action => unit, - onUnmount: (unit => unit) => unit, -} -and oldNewSelf<'state, 'retainedProps, 'action> = { - oldSelf: self<'state, 'retainedProps, 'action>, - newSelf: self<'state, 'retainedProps, 'action>, -} - -type rec jsComponentThis<'state, 'props, 'retainedProps, 'action> = { - "state": totalState<'state, 'retainedProps, 'action>, - "props": {"reasonProps": 'props}, - "setState": @meth ( - ( - totalState<'state, 'retainedProps, 'action>, - 'props, - ) => totalState<'state, 'retainedProps, 'action>, - Js.nullable unit>, - ) => unit, - "jsPropsToReason": option>, -} -/* ** - * `totalState` tracks all of the internal reason API bookkeeping. - * - * Since we will mutate `totalState` in `shouldComponentUpdate`, and since - * there's no guarantee that returning true from `shouldComponentUpdate` - * guarantees that a component's update *actually* takes place (it could get - * rolled back by Fiber etc), then we should put all properties that we - * mutate directly on the totalState, so that when Fiber makes backup shallow - * backup copies of `totalState`, our changes can be rolled back correctly - * even when we mutate them. - */ -and totalState<'state, 'retainedProps, 'action> = {"reasonState": 'state} - -let anyToUnit = _ => () - -let anyToTrue = _ => true - -let willReceivePropsDefault = ({state}) => state - -let renderDefault = _self => string("RenderNotImplemented") - -let initialStateDefault = () => () - -let reducerDefault: ('action, 'state) => update<'state, 'retainedProps, 'action> = ( - _action, - _state, -) => NoUpdate - -let convertPropsIfTheyreFromJs = (props, jsPropsToReason, debugName) => { - let props = Obj.magic(props) - switch (Js.Nullable.toOption(props["reasonProps"]), jsPropsToReason) { - | (Some(props), _) => props - | (None, Some(toReasonProps)) => Element(toReasonProps(props)) - | (None, None) => - throw( - Invalid_argument( - "A JS component called the Reason component " ++ - (debugName ++ - " which didn't implement the JS->Reason React props conversion."), - ), - ) - } -} - -// Old reason-react sources converted from .re syntax. -// The OCaml object system is not available in ReScript anymore -// let createClass = (type reasonState retainedProps action, debugName): reactClass => -// ReasonReactOptimizedCreateClass.createClass(. Pexp_object not implemented in printer) -let createClass = Obj.magic - -let basicComponent = debugName => { - let componentTemplate = { - reactClassInternal: createClass(debugName), - debugName, - /* Keep here as a way to prove that the API may be implemented soundly */ - handedOffState: { - contents: None, - }, - didMount: anyToUnit, - willReceiveProps: willReceivePropsDefault, - didUpdate: anyToUnit, - willUnmount: anyToUnit, - willUpdate: anyToUnit, - /* ** - * Called when component will certainly mount at some point - and may be - * called on the sever for server side React rendering. - */ - shouldUpdate: anyToTrue, - render: renderDefault, - initialState: initialStateDefault, - reducer: reducerDefault, - jsElementWrapped: None, - retainedProps: (), - } - componentTemplate -} - -let statelessComponent = (debugName): component => - basicComponent(debugName) - -let statelessComponentWithRetainedProps = (debugName): componentSpec< - stateless, - stateless, - 'retainedProps, - noRetainedProps, - actionless, -> => basicComponent(debugName) - -let reducerComponent = (debugName): componentSpec< - 'state, - stateless, - noRetainedProps, - noRetainedProps, - 'action, -> => basicComponent(debugName) - -let reducerComponentWithRetainedProps = (debugName): componentSpec< - 'state, - stateless, - 'retainedProps, - noRetainedProps, - 'action, -> => basicComponent(debugName) - -/* ** - * Convenience for creating React elements before we have a better JSX transform. Hopefully this makes it - * usable to build some components while waiting to migrate the JSX transform to the next API. - * - * Constrain the component here instead of relying on the Element constructor which would lead to confusing - * error messages. - */ -let element = ( - ~key: string=Obj.magic(Js.Nullable.undefined), - ~ref: Js.nullable => unit=Obj.magic(Js.Nullable.undefined), - component: component<'state, 'retainedProps, 'action>, -) => { - let element = Element(component) - switch component.jsElementWrapped { - | Some(jsElementWrapped) => - jsElementWrapped(~key=Js.Nullable.return(key), ~ref=Js.Nullable.return(ref)) - | None => - createElement( - component.reactClassInternal, - ~props={"key": key, "ref": ref, "reasonProps": element}, - [], - ) - } -} - -let wrapReasonForJs = ( - ~component, - jsPropsToReason: jsPropsToReason<'jsProps, 'state, 'retainedProps, 'action>, -) => { - let jsPropsToReason: jsPropsToReason = Obj.magic( - jsPropsToReason, - ) /* cast 'jsProps to jsProps */ - let uncurriedJsPropsToReason: uncurriedJsPropsToReason< - jsProps, - 'state, - 'retainedProps, - 'action, - > = jsProps => jsPropsToReason(jsProps) - Obj.magic(component.reactClassInternal)["prototype"]["jsPropsToReason"] = Some( - uncurriedJsPropsToReason, - ) - component.reactClassInternal -} - -module WrapProps = { - /* We wrap the props for reason->reason components, as a marker that "these props were passed from another - reason component" */ - let wrapProps = ( - ~reactClass, - ~props, - children, - ~key: Js.nullable, - ~ref: Js.nullable => unit>, - ) => { - let props = Js.Obj.assign( - Js.Obj.assign(Js.Obj.empty(), Obj.magic(props)), - {"ref": ref, "key": key}, - ) - let varargs = Js.Array.concat(Obj.magic(children), [Obj.magic(reactClass), Obj.magic(props)]) - /* Use varargs under the hood */ - Obj.magic(createElementVerbatim)["apply"](Js.Nullable.null, varargs) - } - let dummyInteropComponent = basicComponent("interop") - let wrapJsForReason = (~reactClass, ~props, children): component< - stateless, - noRetainedProps, - _, - > => { - let jsElementWrapped = Some(wrapProps(~reactClass, ~props, children, ...)) - {...dummyInteropComponent, jsElementWrapped} - } -} - -let wrapJsForReason = WrapProps.wrapJsForReason - -@module("react") external fragment: 'a = "Fragment" - -module Router = ReasonReactRouter diff --git a/tests/tests/src/reasonReact.resi b/tests/tests/src/reasonReact.resi deleted file mode 100644 index 89d3b7f58a..0000000000 --- a/tests/tests/src/reasonReact.resi +++ /dev/null @@ -1,214 +0,0 @@ -/* ** - * This API assumes that JSX will desugar into: - * - * ReasonReact.element( - * Foo.make(~key, ~ref, ~attr1=val1, ~attrn=valn, [| |] - * ) - */ - -type reactClass - -type reactElement = React.element - -type reactRef - -@val external null: reactElement = "null" - -external string: string => reactElement = "%identity" - -external array: array => reactElement = "%identity" - -external refToJsObj: reactRef => {..} = "%identity" - -/* This should _not_ be used directly, unless you're passing a class like this: - - switch (actionsClass) { - | Some(actions) => - ReasonReact.createElement( - actions, - ~props={ - "className": "hi" - }, - [|whatever|], - ) - } - - In every other case, you should be using the JSX - */ -@variadic @val @module("react") -external createElement: (reactClass, ~props: {..}=?, array) => reactElement = - "createElement" - -@variadic @module("react") -external cloneElement: (reactElement, ~props: {..}=?, array) => reactElement = - "cloneElement" - -type renderNotImplemented = RenderNotImplemented - -/* ** - * A stateless component is a component with state of type unit. This cannot be - * abstract for now, because a stateless component's willReceiveProps needs to - * return the state, aka unit. We can provide a helper - * ReasonReact.statelessReturn that's of type `stateless`, but that's verbose - */ -type stateless = unit - -type noRetainedProps - -/* ** An actionless component is a component with actions of type unit */ -type actionless = unit - -/* Control how a state update is performed. - The state can be updated of left unchanged. - Side effects can be specified and are scheduled for later execution. - Note: when a side effect is scheduled, it's added to a queue of existing pending side effects. - All the side effects are performed in batch after all the state updates have been performed, - in the same order in which they were scheduled, just before shouldUpdate is called. */ -type rec update<'state, 'retainedProps, 'action> = - | /* Don't update the state. */ - NoUpdate - /* Update the state with the given one. */ - | Update('state) - /* Perform side effects without updating state, and don't trigger a re-render. - Do not prevent a re-render either, if one was scheduled to happen. - The side effects function is invoked when all the updates have completed. */ - | SideEffects(self<'state, 'retainedProps, 'action> => unit) - /* Update the state and perform side effects. - The side effects function is invoked when all the updates have completed. */ - | UpdateWithSideEffects('state, self<'state, 'retainedProps, 'action> => unit) -and self<'state, 'retainedProps, 'action> = { - /* ** - * Call a handler function. - * - * The callback is passed the payload and current state immediately. - * Note: the callback typically performs side effects, since it returns nothing. - */ - handle: 'payload. (('payload, self<'state, 'retainedProps, 'action>) => unit, 'payload) => unit, - state: 'state, - retainedProps: 'retainedProps, - send: 'action => unit, - onUnmount: (unit => unit) => unit, -} - -type reactClassInternal - -/* ** For internal use only */ -type jsElementWrapped - -type oldNewSelf<'state, 'retainedProps, 'action> = { - oldSelf: self<'state, 'retainedProps, 'action>, - newSelf: self<'state, 'retainedProps, 'action>, -} - -type rec componentSpec<'state, 'initialState, 'retainedProps, 'initialRetainedProps, 'action> = { - debugName: string, - reactClassInternal: reactClassInternal, - /* Keep here as a way to prove that the API may be implemented soundly */ - mutable handedOffState: ref>, - /* ** Callback invoked when the component receives new props or state. - * Note: this callback must not perform side effects. - */ - willReceiveProps: self<'state, 'retainedProps, 'action> => 'state, - didMount: self<'state, 'retainedProps, 'action> => unit, - didUpdate: oldNewSelf<'state, 'retainedProps, 'action> => unit, - willUnmount: self<'state, 'retainedProps, 'action> => unit, - willUpdate: oldNewSelf<'state, 'retainedProps, 'action> => unit, - shouldUpdate: oldNewSelf<'state, 'retainedProps, 'action> => bool, - render: self<'state, 'retainedProps, 'action> => reactElement, - initialState: unit => 'initialState, - retainedProps: 'initialRetainedProps, - /* ** Reducer callback. - * - * The callback is invoked by the reduce function contained in self. - * A state update is scheduled based on the action passed, and added to the queue of pending updates. - * The state received will be the resulting one after the pending updates have been executed. - * - * Note: this callback must not perform side effects. - * If side effects are required, they should be contained in a - * side-effectful function specified in the returned update. - */ - reducer: ('action, 'state) => update<'state, 'retainedProps, 'action>, - jsElementWrapped: jsElementWrapped, -} -and component<'state, 'retainedProps, 'action> = componentSpec< - 'state, - 'state, - 'retainedProps, - 'retainedProps, - 'action, -> - -/* ** Create a stateless component: i.e. a component where state has type stateless. */ -let statelessComponent: string => componentSpec< - stateless, - stateless, - noRetainedProps, - noRetainedProps, - actionless, -> - -let statelessComponentWithRetainedProps: string => componentSpec< - stateless, - stateless, - 'retainedProps, - noRetainedProps, - actionless, -> - -let reducerComponent: string => componentSpec< - 'state, - stateless, - noRetainedProps, - noRetainedProps, - 'action, -> - -let reducerComponentWithRetainedProps: string => componentSpec< - 'state, - stateless, - 'retainedProps, - noRetainedProps, - 'action, -> - -let element: ( - ~key: string=?, - ~ref: Js.nullable => unit=?, - component<'state, 'retainedProps, 'action>, -) => reactElement - -type jsPropsToReason<'jsProps, 'state, 'retainedProps, 'action> = 'jsProps => component< - 'state, - 'retainedProps, - 'action, -> - -/* ** - * We *under* constrain the kind of component spec this accepts because we actually extend the *originally* - * defined component. It uses mutation on the original component, so that even if it is extended with - * {...component}, all extensions will also see the underlying js class. I can sleep at night because js - * interop is integrating with untyped, code and it is *possible* to create pure-ReasonReact apps without JS - * interop entirely. */ -let wrapReasonForJs: ( - ~component: componentSpec<'state, 'initialState, 'retainedProps, 'initialRetainedProps, 'action>, - jsPropsToReason<_>, -) => reactClass - -@deprecated(" -Were you using this because you needed to pass a children array reference to a DOM element? We now support children spread for DOM elements: `
...children
`. -Alternatively, if you're using this because the prop name contains a hyphen, please use `ReactDOMRe.createElementVariadic` instead.") -let createDomElement: (string, ~props: {..}, array) => reactElement - -/** - * Wrap props into a JS component - * Use for interop when Reason components use JS components - */ -let wrapJsForReason: ( - ~reactClass: reactClass, - ~props: 'a, - 'b, -) => component - -@module("react") external fragment: 'a = "Fragment" - -module Router = ReasonReactRouter diff --git a/tests/tests/src/reasonReactCompat.mjs b/tests/tests/src/reasonReactCompat.mjs deleted file mode 100644 index 0080545c14..0000000000 --- a/tests/tests/src/reasonReactCompat.mjs +++ /dev/null @@ -1,13 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as ReasonReact from "./reasonReact.mjs"; - -let wrapReactForReasonReact = ReasonReact.wrapJsForReason; - -let wrapReasonReactForReact = ReasonReact.wrapReasonForJs; - -export { - wrapReactForReasonReact, - wrapReasonReactForReact, -} -/* ReasonReact Not a pure module */ diff --git a/tests/tests/src/reasonReactCompat.res b/tests/tests/src/reasonReactCompat.res deleted file mode 100644 index 3118ab160c..0000000000 --- a/tests/tests/src/reasonReactCompat.res +++ /dev/null @@ -1,10 +0,0 @@ -external componentToReasonReactClass: React.component<'props> => ReasonReact.reactClass = - "%identity" - -external reasonReactClassToComponent: ReasonReact.reactClass => React.component<'props> = - "%identity" -let wrapReactForReasonReact = (component, props, children) => - ReasonReact.wrapJsForReason(~reactClass=componentToReasonReactClass(component), ~props, children) - -let wrapReasonReactForReact = (~component, propsConverter) => - reasonReactClassToComponent(ReasonReact.wrapReasonForJs(~component, propsConverter)) diff --git a/tests/tests/src/reasonReactCompat.resi b/tests/tests/src/reasonReactCompat.resi deleted file mode 100644 index bb6e9c257c..0000000000 --- a/tests/tests/src/reasonReactCompat.resi +++ /dev/null @@ -1,14 +0,0 @@ -let wrapReactForReasonReact: ( - React.component<'props>, - 'props, - 'children, -) => ReasonReact.component< - ReasonReact.stateless, - ReasonReact.noRetainedProps, - ReasonReact.actionless, -> - -let wrapReasonReactForReact: ( - ~component: ReasonReact.componentSpec<'a, 'b, 'c, 'd, 'e>, - ReasonReact.jsPropsToReason<'props, 'g, 'h, 'i>, -) => React.component<'props> diff --git a/tests/tests/src/reasonReactOptimizedCreateClass.mjs b/tests/tests/src/reasonReactOptimizedCreateClass.mjs deleted file mode 100644 index 1f5612b73c..0000000000 --- a/tests/tests/src/reasonReactOptimizedCreateClass.mjs +++ /dev/null @@ -1,894 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as React from "react"; - -function _assign(prim0, prim1) { - return Object.assign(prim0, prim1); -} - -let emptyObject = {}; - -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -// 'use strict'; - -// var _assign = require('object-assign'); - -// var emptyObject = require('emptyObject'); -// var _invariant = require('invariant'); - -// if (process.env.NODE_ENV !== 'production') { -// var warning = require('fbjs/lib/warning'); -// } - -var MIXINS_KEY = 'mixins'; - -// Helper function to allow the creation of anonymous functions which do not -// have .name set to the name of the variable being assigned to. -function identity(fn) { - return fn; -} - -var ReactPropTypeLocationNames; -// if (process.env.NODE_ENV !== 'production') { -// ReactPropTypeLocationNames = { -// prop: 'prop', -// context: 'context', -// childContext: 'child context' -// }; -// } else { - ReactPropTypeLocationNames = {}; -// } -; - -let factory = (function factory(ReactComponent, isValidElement, ReactNoopUpdateQueue) { - /** - * Policies that describe methods in \`ReactClassInterface\`. - */ - - var injectedMixins = []; - - /** - * Composite components are higher-level components that compose other composite - * or host components. - * - * To create a new type of \`ReactClass\`, pass a specification of - * your new class to \`React.createClass\`. The only requirement of your class - * specification is that you implement a \`render\` method. - * - * var MyComponent = React.createClass({ - * render: function() { - * return
Hello World
; - * } - * }); - * - * The class specification supports a specific protocol of methods that have - * special meaning (e.g. \`render\`). See \`ReactClassInterface\` for - * more the comprehensive protocol. Any other properties and methods in the - * class specification will be available on the prototype. - * - * @interface ReactClassInterface - * @internal - */ - var ReactClassInterface = { - /** - * An array of Mixin objects to include when defining your component. - * - * @type {array} - * @optional - */ - mixins: 'DEFINE_MANY', - - /** - * An object containing properties and methods that should be defined on - * the component's constructor instead of its prototype (static methods). - * - * @type {object} - * @optional - */ - statics: 'DEFINE_MANY', - - /** - * Definition of prop types for this component. - * - * @type {object} - * @optional - */ - propTypes: 'DEFINE_MANY', - - /** - * Definition of context types for this component. - * - * @type {object} - * @optional - */ - contextTypes: 'DEFINE_MANY', - - /** - * Definition of context types this component sets for its children. - * - * @type {object} - * @optional - */ - childContextTypes: 'DEFINE_MANY', - - // ==== Definition methods ==== - - /** - * Invoked when the component is mounted. Values in the mapping will be set on - * \`this.props\` if that prop is not specified (i.e. using an \`in\` check). - * - * This method is invoked before \`getInitialState\` and therefore cannot rely - * on \`this.state\` or use \`this.setState\`. - * - * @return {object} - * @optional - */ - getDefaultProps: 'DEFINE_MANY_MERGED', - - /** - * Invoked once before the component is mounted. The return value will be used - * as the initial value of \`this.state\`. - * - * getInitialState: function() { - * return { - * isOn: false, - * fooBaz: new BazFoo() - * } - * } - * - * @return {object} - * @optional - */ - getInitialState: 'DEFINE_MANY_MERGED', - - /** - * @return {object} - * @optional - */ - getChildContext: 'DEFINE_MANY_MERGED', - - /** - * Uses props from \`this.props\` and state from \`this.state\` to render the - * structure of the component. - * - * No guarantees are made about when or how often this method is invoked, so - * it must not have side effects. - * - * render: function() { - * var name = this.props.name; - * return
Hello, {name}!
; - * } - * - * @return {ReactComponent} - * @required - */ - render: 'DEFINE_ONCE', - - // ==== Delegate methods ==== - - /** - * Invoked when the component is initially created and about to be mounted. - * This may have side effects, but any external subscriptions or data created - * by this method must be cleaned up in \`componentWillUnmount\`. - * - * @optional - */ - componentWillMount: 'DEFINE_MANY', - - /** - * Invoked when the component has been mounted and has a DOM representation. - * However, there is no guarantee that the DOM node is in the document. - * - * Use this as an opportunity to operate on the DOM when the component has - * been mounted (initialized and rendered) for the first time. - * - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidMount: 'DEFINE_MANY', - - /** - * Invoked before the component receives new props. - * - * Use this as an opportunity to react to a prop transition by updating the - * state using \`this.setState\`. Current props are accessed via \`this.props\`. - * - * componentWillReceiveProps: function(nextProps, nextContext) { - * this.setState({ - * likesIncreasing: nextProps.likeCount > this.props.likeCount - * }); - * } - * - * NOTE: There is no equivalent \`componentWillReceiveState\`. An incoming prop - * transition may cause a state change, but the opposite is not true. If you - * need it, you are probably looking for \`componentWillUpdate\`. - * - * @param {object} nextProps - * @optional - */ - componentWillReceiveProps: 'DEFINE_MANY', - - /** - * Invoked while deciding if the component should be updated as a result of - * receiving new props, state and/or context. - * - * Use this as an opportunity to \`return false\` when you're certain that the - * transition to the new props/state/context will not require a component - * update. - * - * shouldComponentUpdate: function(nextProps, nextState, nextContext) { - * return !equal(nextProps, this.props) || - * !equal(nextState, this.state) || - * !equal(nextContext, this.context); - * } - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @return {boolean} True if the component should update. - * @optional - */ - shouldComponentUpdate: 'DEFINE_ONCE', - - /** - * Invoked when the component is about to update due to a transition from - * \`this.props\`, \`this.state\` and \`this.context\` to \`nextProps\`, \`nextState\` - * and \`nextContext\`. - * - * Use this as an opportunity to perform preparation before an update occurs. - * - * NOTE: You **cannot** use \`this.setState()\` in this method. - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @param {ReactReconcileTransaction} transaction - * @optional - */ - componentWillUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component's DOM representation has been updated. - * - * Use this as an opportunity to operate on the DOM when the component has - * been updated. - * - * @param {object} prevProps - * @param {?object} prevState - * @param {?object} prevContext - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component is about to be removed from its parent and have - * its DOM representation destroyed. - * - * Use this as an opportunity to deallocate any external resources. - * - * NOTE: There is no \`componentDidUnmount\` since your component will have been - * destroyed by that point. - * - * @optional - */ - componentWillUnmount: 'DEFINE_MANY', - - // ==== Advanced methods ==== - - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @internal - * @overridable - */ - updateComponent: 'OVERRIDE_BASE' - }; - - /** - * Mapping from class specification keys to special processing functions. - * - * Although these are declared like instance properties in the specification - * when defining classes using \`React.createClass\`, they are actually static - * and are accessible on the constructor instead of the prototype. Despite - * being static, they must be defined outside of the "statics" key under - * which all other static methods are defined. - */ - var RESERVED_SPEC_KEYS = { - displayName: function(Constructor, displayName) { - Constructor.displayName = displayName; - }, - mixins: function(Constructor, mixins) { - if (mixins) { - for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(Constructor, mixins[i]); - } - } - }, - childContextTypes: function(Constructor, childContextTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, childContextTypes, 'childContext'); - // } - Constructor.childContextTypes = _assign( - {}, - Constructor.childContextTypes, - childContextTypes - ); - }, - contextTypes: function(Constructor, contextTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, contextTypes, 'context'); - // } - Constructor.contextTypes = _assign( - {}, - Constructor.contextTypes, - contextTypes - ); - }, - /** - * Special case getDefaultProps which should move into statics but requires - * automatic merging. - */ - getDefaultProps: function(Constructor, getDefaultProps) { - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps = createMergedResultFunction( - Constructor.getDefaultProps, - getDefaultProps - ); - } else { - Constructor.getDefaultProps = getDefaultProps; - } - }, - propTypes: function(Constructor, propTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, propTypes, 'prop'); - // } - Constructor.propTypes = _assign({}, Constructor.propTypes, propTypes); - }, - statics: function(Constructor, statics) { - mixStaticSpecIntoComponent(Constructor, statics); - }, - autobind: function() {} - }; - - function validateTypeDef(Constructor, typeDef, location) { - for (var propName in typeDef) { - // if (typeDef.hasOwnProperty(propName)) { - // // use a warning instead of an _invariant so components - // // don't show up in prod but only in __DEV__ - // // if (process.env.NODE_ENV !== 'production') { - // // warning( - // // typeof typeDef[propName] === 'function', - // // '%s: %s type \`%s\` is invalid; it must be a function, usually from ' + - // // 'React.PropTypes.', - // // Constructor.displayName || 'ReactClass', - // // ReactPropTypeLocationNames[location], - // // propName - // // ); - // // } - // } - } - } - - function validateMethodOverride(isAlreadyDefined, name) { - var specPolicy = ReactClassInterface.hasOwnProperty(name) - ? ReactClassInterface[name] - : null; - - // Disallow overriding of base class methods unless explicitly allowed. - if (ReactClassMixin.hasOwnProperty(name)) { - // _invariant( - // specPolicy === 'OVERRIDE_BASE', - // 'ReactClassInterface: You are attempting to override ' + - // '\`%s\` from your class specification. Ensure that your method names ' + - // 'do not overlap with React methods.', - // name - // ); - } - - // Disallow defining methods more than once unless explicitly allowed. - if (isAlreadyDefined) { - // _invariant( - // specPolicy === 'DEFINE_MANY' || specPolicy === 'DEFINE_MANY_MERGED', - // 'ReactClassInterface: You are attempting to define ' + - // '\`%s\` on your component more than once. This conflict may be due ' + - // 'to a mixin.', - // name - // ); - } - } - - /** - * Mixin helper which handles policy validation and reserved - * specification keys when building React classes. - */ - function mixSpecIntoComponent(Constructor, spec) { - if (!spec) { - // if (process.env.NODE_ENV !== 'production') { - // var typeofSpec = typeof spec; - // var isMixinValid = typeofSpec === 'object' && spec !== null; - // - // if (process.env.NODE_ENV !== 'production') { - // warning( - // isMixinValid, - // "%s: You're attempting to include a mixin that is either null " + - // 'or not an object. Check the mixins included by the component, ' + - // 'as well as any mixins they include themselves. ' + - // 'Expected object but got %s.', - // Constructor.displayName || 'ReactClass', - // spec === null ? null : typeofSpec - // ); - // } - // } - - return; - } - - // _invariant( - // typeof spec !== 'function', - // "ReactClass: You're attempting to " + - // 'use a component class or function as a mixin. Instead, just use a ' + - // 'regular object.' - // ); - // _invariant( - // !isValidElement(spec), - // "ReactClass: You're attempting to " + - // 'use a component as a mixin. Instead, just use a regular object.' - // ); - - var proto = Constructor.prototype; - var autoBindPairs = proto.__reactAutoBindPairs; - - // By handling mixins before any other properties, we ensure the same - // chaining order is applied to methods with DEFINE_MANY policy, whether - // mixins are listed before or after these methods in the spec. - if (spec.hasOwnProperty(MIXINS_KEY)) { - RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins); - } - - for (var name in spec) { - if (!spec.hasOwnProperty(name)) { - continue; - } - - if (name === MIXINS_KEY) { - // We have already handled mixins in a special case above. - continue; - } - - var property = spec[name]; - var isAlreadyDefined = proto.hasOwnProperty(name); - validateMethodOverride(isAlreadyDefined, name); - - if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](Constructor, property); - } else { - // Setup methods on prototype: - // The following member methods should not be automatically bound: - // 1. Expected ReactClass methods (in the "interface"). - // 2. Overridden methods (that were mixed in). - var isReactClassMethod = ReactClassInterface.hasOwnProperty(name); - var isFunction = typeof property === 'function'; - var shouldAutoBind = - isFunction && - !isReactClassMethod && - !isAlreadyDefined && - spec.autobind !== false; - - if (shouldAutoBind) { - autoBindPairs.push(name, property); - proto[name] = property; - } else { - if (isAlreadyDefined) { - var specPolicy = ReactClassInterface[name]; - - // These cases should already be caught by validateMethodOverride. - // _invariant( - // isReactClassMethod && - // (specPolicy === 'DEFINE_MANY_MERGED' || - // specPolicy === 'DEFINE_MANY'), - // 'ReactClass: Unexpected spec policy %s for key %s ' + - // 'when mixing in component specs.', - // specPolicy, - // name - // ); - - // For methods which are defined more than once, call the existing - // methods before calling the new property, merging if appropriate. - if (specPolicy === 'DEFINE_MANY_MERGED') { - proto[name] = createMergedResultFunction(proto[name], property); - } else if (specPolicy === 'DEFINE_MANY') { - proto[name] = createChainedFunction(proto[name], property); - } - } else { - proto[name] = property; - // if (process.env.NODE_ENV !== 'production') { - // // Add verbose displayName to the function, which helps when looking - // // at profiling tools. - // if (typeof property === 'function' && spec.displayName) { - // proto[name].displayName = spec.displayName + '_' + name; - // } - // } - } - } - } - } - } - - function mixStaticSpecIntoComponent(Constructor, statics) { - if (!statics) { - return; - } - for (var name in statics) { - var property = statics[name]; - if (!statics.hasOwnProperty(name)) { - continue; - } - - var isReserved = name in RESERVED_SPEC_KEYS; - // _invariant( - // !isReserved, - // 'ReactClass: You are attempting to define a reserved ' + - // 'property, \`%s\`, that shouldn\\'t be on the "statics" key. Define it ' + - // 'as an instance property instead; it will still be accessible on the ' + - // 'constructor.', - // name - // ); - - var isInherited = name in Constructor; - // _invariant( - // !isInherited, - // 'ReactClass: You are attempting to define ' + - // '\`%s\` on your component more than once. This conflict may be ' + - // 'due to a mixin.', - // name - // ); - Constructor[name] = property; - } - } - - /** - * Merge two objects, but throw if both contain the same key. - * - * @param {object} one The first object, which is mutated. - * @param {object} two The second object - * @return {object} one after it has been mutated to contain everything in two. - */ - function mergeIntoWithNoDuplicateKeys(one, two) { - // _invariant( - // one && two && typeof one === 'object' && typeof two === 'object', - // 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.' - // ); - - for (var key in two) { - if (two.hasOwnProperty(key)) { - // _invariant( - // one[key] === undefined, - // 'mergeIntoWithNoDuplicateKeys(): ' + - // 'Tried to merge two objects with the same key: \`%s\`. This conflict ' + - // 'may be due to a mixin; in particular, this may be caused by two ' + - // 'getInitialState() or getDefaultProps() methods returning objects ' + - // 'with clashing keys.', - // key - // ); - one[key] = two[key]; - } - } - return one; - } - - /** - * Creates a function that invokes two functions and merges their return values. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ - function createMergedResultFunction(one, two) { - return function mergedResult() { - var a = one.apply(this, arguments); - var b = two.apply(this, arguments); - if (a == null) { - return b; - } else if (b == null) { - return a; - } - var c = {}; - mergeIntoWithNoDuplicateKeys(c, a); - mergeIntoWithNoDuplicateKeys(c, b); - return c; - }; - } - - /** - * Creates a function that invokes two functions and ignores their return vales. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ - function createChainedFunction(one, two) { - return function chainedFunction() { - one.apply(this, arguments); - two.apply(this, arguments); - }; - } - - /** - * Binds a method to the component. - * - * @param {object} component Component whose method is going to be bound. - * @param {function} method Method to be bound. - * @return {function} The bound method. - */ - function bindAutoBindMethod(component, method) { - var boundMethod = method.bind(component); - // if (process.env.NODE_ENV !== 'production') { - // boundMethod.__reactBoundContext = component; - // boundMethod.__reactBoundMethod = method; - // boundMethod.__reactBoundArguments = null; - // var componentName = component.constructor.displayName; - // var _bind = boundMethod.bind; - // boundMethod.bind = function(newThis) { - // for ( - // var _len = arguments.length, - // args = Array(_len > 1 ? _len - 1 : 0), - // _key = 1; - // _key < _len; - // _key++ - // ) { - // args[_key - 1] = arguments[_key]; - // } - // - // // User is trying to bind() an autobound method; we effectively will - // // ignore the value of "this" that the user is trying to use, so - // // let's warn. - // if (newThis !== component && newThis !== null) { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // false, - // 'bind(): React component methods may only be bound to the ' + - // 'component instance. See %s', - // componentName - // ); - // } - // } else if (!args.length) { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // false, - // 'bind(): You are binding a component method to the component. ' + - // 'React does this for you automatically in a high-performance ' + - // 'way, so you can safely remove this call. See %s', - // componentName - // ); - // } - // return boundMethod; - // } - // var reboundMethod = _bind.apply(boundMethod, arguments); - // reboundMethod.__reactBoundContext = component; - // reboundMethod.__reactBoundMethod = method; - // reboundMethod.__reactBoundArguments = args; - // return reboundMethod; - // }; - // } - return boundMethod; - } - - /** - * Binds all auto-bound methods in a component. - * - * @param {object} component Component whose method is going to be bound. - */ - function bindAutoBindMethods(component) { - var pairs = component.__reactAutoBindPairs; - for (var i = 0; i < pairs.length; i += 2) { - var autoBindKey = pairs[i]; - var method = pairs[i + 1]; - component[autoBindKey] = bindAutoBindMethod(component, method); - } - } - - var IsMountedPreMixin = { - componentDidMount: function() { - this.__isMounted = true; - } - }; - - var IsMountedPostMixin = { - componentWillUnmount: function() { - this.__isMounted = false; - } - }; - - /** - * Add more to the ReactClass base class. These are all legacy features and - * therefore not already part of the modern ReactComponent. - */ - var ReactClassMixin = { - /** - * TODO: This will be deprecated because state should always keep a consistent - * type signature and the only use case for this, is to avoid that. - */ - replaceState: function(newState, callback) { - this.updater.enqueueReplaceState(this, newState, callback); - }, - - /** - * Checks whether or not this composite component is mounted. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function() { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // this.__didWarnIsMounted, - // '%s: isMounted is deprecated. Instead, make sure to clean up ' + - // 'subscriptions and pending requests in componentWillUnmount to ' + - // 'prevent memory leaks.', - // (this.constructor && this.constructor.displayName) || - // this.name || - // 'Component' - // ); - // this.__didWarnIsMounted = true; - // } - return !!this.__isMounted; - } - }; - - var ReactClassComponent = function() {}; - _assign( - ReactClassComponent.prototype, - ReactComponent.prototype, - ReactClassMixin - ); - - /** - * Creates a composite component class given a class specification. - * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass - * - * @param {object} spec Class specification (which must define \`render\`). - * @return {function} Component constructor function. - * @public - */ - function createClass(spec) { - // To keep our warnings more understandable, we'll use a little hack here to - // ensure that Constructor.name !== 'Constructor'. This makes sure we don't - // unnecessarily identify a class without displayName as 'Constructor'. - var Constructor = identity(function(props, context, updater) { - // This constructor gets overridden by mocks. The argument is used - // by mocks to assert on what gets mounted. - - // if (process.env.NODE_ENV !== 'production') { - // warning( - // this instanceof Constructor, - // 'Something is calling a React component directly. Use a factory or ' + - // 'JSX instead. See: https://fb.me/react-legacyfactory' - // ); - // } - - // Wire up auto-binding - if (this.__reactAutoBindPairs.length) { - bindAutoBindMethods(this); - } - - this.props = props; - this.context = context; - this.refs = emptyObject; - this.updater = updater || ReactNoopUpdateQueue; - - this.state = null; - - // ReactClasses doesn't have constructors. Instead, they use the - // getInitialState and componentWillMount methods for initialization. - - var initialState = this.getInitialState ? this.getInitialState() : null; - // if (process.env.NODE_ENV !== 'production') { - // // We allow auto-mocks to proceed as if they're returning null. - // if ( - // initialState === undefined && - // this.getInitialState._isMockFunction - // ) { - // // This is probably bad practice. Consider warning here and - // // deprecating this convenience. - // initialState = null; - // } - // } - // _invariant( - // typeof initialState === 'object' && !Array.isArray(initialState), - // '%s.getInitialState(): must return an object or null', - // Constructor.displayName || 'ReactCompositeComponent' - // ); - - this.state = initialState; - }); - Constructor.prototype = new ReactClassComponent(); - Constructor.prototype.constructor = Constructor; - Constructor.prototype.__reactAutoBindPairs = []; - - injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor)); - - mixSpecIntoComponent(Constructor, IsMountedPreMixin); - mixSpecIntoComponent(Constructor, spec); - mixSpecIntoComponent(Constructor, IsMountedPostMixin); - - // Initialize the defaultProps property after all mixins have been merged. - if (Constructor.getDefaultProps) { - Constructor.defaultProps = Constructor.getDefaultProps(); - } - - // if (process.env.NODE_ENV !== 'production') { - // // This is a tag to indicate that the use of these method names is ok, - // // since it's used with createClass. If it's not, then it's likely a - // // mistake so we'll warn you to use the static property, property - // // initializer or constructor respectively. - // if (Constructor.getDefaultProps) { - // Constructor.getDefaultProps.isReactClassApproved = {}; - // } - // if (Constructor.prototype.getInitialState) { - // Constructor.prototype.getInitialState.isReactClassApproved = {}; - // } - // } - - // _invariant( - // Constructor.prototype.render, - // 'createClass(...): Class specification must implement a \`render\` method.' - // ); - - // if (process.env.NODE_ENV !== 'production') { - // warning( - // !Constructor.prototype.componentShouldUpdate, - // '%s has a method called ' + - // 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - // 'The name is phrased as a question because the function is ' + - // 'expected to return a value.', - // spec.displayName || 'A component' - // ); - // warning( - // !Constructor.prototype.componentWillRecieveProps, - // '%s has a method called ' + - // 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', - // spec.displayName || 'A component' - // ); - // } - - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactClassInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } - - return Constructor; - } - - return createClass; -}); - -let reactNoopUpdateQueue = new React.Component().updater; - -let createClass = factory(React.Component, React.isValidElement, reactNoopUpdateQueue); - -export { - _assign, - emptyObject, - factory, - reactNoopUpdateQueue, - createClass, -} -/* Not a pure module */ diff --git a/tests/tests/src/reasonReactOptimizedCreateClass.res b/tests/tests/src/reasonReactOptimizedCreateClass.res deleted file mode 100644 index 34350c989c..0000000000 --- a/tests/tests/src/reasonReactOptimizedCreateClass.res +++ /dev/null @@ -1,889 +0,0 @@ -let _assign = Js.Obj.assign - -let emptyObject = Js.Obj.empty() - -%%raw(` -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -// 'use strict'; - -// var _assign = require('object-assign'); - -// var emptyObject = require('emptyObject'); -// var _invariant = require('invariant'); - -// if (process.env.NODE_ENV !== 'production') { -// var warning = require('fbjs/lib/warning'); -// } - -var MIXINS_KEY = 'mixins'; - -// Helper function to allow the creation of anonymous functions which do not -// have .name set to the name of the variable being assigned to. -function identity(fn) { - return fn; -} - -var ReactPropTypeLocationNames; -// if (process.env.NODE_ENV !== 'production') { -// ReactPropTypeLocationNames = { -// prop: 'prop', -// context: 'context', -// childContext: 'child context' -// }; -// } else { - ReactPropTypeLocationNames = {}; -// } -`) - -let factory = %raw(` -function factory(ReactComponent, isValidElement, ReactNoopUpdateQueue) { - /** - * Policies that describe methods in \`ReactClassInterface\`. - */ - - var injectedMixins = []; - - /** - * Composite components are higher-level components that compose other composite - * or host components. - * - * To create a new type of \`ReactClass\`, pass a specification of - * your new class to \`React.createClass\`. The only requirement of your class - * specification is that you implement a \`render\` method. - * - * var MyComponent = React.createClass({ - * render: function() { - * return
Hello World
; - * } - * }); - * - * The class specification supports a specific protocol of methods that have - * special meaning (e.g. \`render\`). See \`ReactClassInterface\` for - * more the comprehensive protocol. Any other properties and methods in the - * class specification will be available on the prototype. - * - * @interface ReactClassInterface - * @internal - */ - var ReactClassInterface = { - /** - * An array of Mixin objects to include when defining your component. - * - * @type {array} - * @optional - */ - mixins: 'DEFINE_MANY', - - /** - * An object containing properties and methods that should be defined on - * the component's constructor instead of its prototype (static methods). - * - * @type {object} - * @optional - */ - statics: 'DEFINE_MANY', - - /** - * Definition of prop types for this component. - * - * @type {object} - * @optional - */ - propTypes: 'DEFINE_MANY', - - /** - * Definition of context types for this component. - * - * @type {object} - * @optional - */ - contextTypes: 'DEFINE_MANY', - - /** - * Definition of context types this component sets for its children. - * - * @type {object} - * @optional - */ - childContextTypes: 'DEFINE_MANY', - - // ==== Definition methods ==== - - /** - * Invoked when the component is mounted. Values in the mapping will be set on - * \`this.props\` if that prop is not specified (i.e. using an \`in\` check). - * - * This method is invoked before \`getInitialState\` and therefore cannot rely - * on \`this.state\` or use \`this.setState\`. - * - * @return {object} - * @optional - */ - getDefaultProps: 'DEFINE_MANY_MERGED', - - /** - * Invoked once before the component is mounted. The return value will be used - * as the initial value of \`this.state\`. - * - * getInitialState: function() { - * return { - * isOn: false, - * fooBaz: new BazFoo() - * } - * } - * - * @return {object} - * @optional - */ - getInitialState: 'DEFINE_MANY_MERGED', - - /** - * @return {object} - * @optional - */ - getChildContext: 'DEFINE_MANY_MERGED', - - /** - * Uses props from \`this.props\` and state from \`this.state\` to render the - * structure of the component. - * - * No guarantees are made about when or how often this method is invoked, so - * it must not have side effects. - * - * render: function() { - * var name = this.props.name; - * return
Hello, {name}!
; - * } - * - * @return {ReactComponent} - * @required - */ - render: 'DEFINE_ONCE', - - // ==== Delegate methods ==== - - /** - * Invoked when the component is initially created and about to be mounted. - * This may have side effects, but any external subscriptions or data created - * by this method must be cleaned up in \`componentWillUnmount\`. - * - * @optional - */ - componentWillMount: 'DEFINE_MANY', - - /** - * Invoked when the component has been mounted and has a DOM representation. - * However, there is no guarantee that the DOM node is in the document. - * - * Use this as an opportunity to operate on the DOM when the component has - * been mounted (initialized and rendered) for the first time. - * - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidMount: 'DEFINE_MANY', - - /** - * Invoked before the component receives new props. - * - * Use this as an opportunity to react to a prop transition by updating the - * state using \`this.setState\`. Current props are accessed via \`this.props\`. - * - * componentWillReceiveProps: function(nextProps, nextContext) { - * this.setState({ - * likesIncreasing: nextProps.likeCount > this.props.likeCount - * }); - * } - * - * NOTE: There is no equivalent \`componentWillReceiveState\`. An incoming prop - * transition may cause a state change, but the opposite is not true. If you - * need it, you are probably looking for \`componentWillUpdate\`. - * - * @param {object} nextProps - * @optional - */ - componentWillReceiveProps: 'DEFINE_MANY', - - /** - * Invoked while deciding if the component should be updated as a result of - * receiving new props, state and/or context. - * - * Use this as an opportunity to \`return false\` when you're certain that the - * transition to the new props/state/context will not require a component - * update. - * - * shouldComponentUpdate: function(nextProps, nextState, nextContext) { - * return !equal(nextProps, this.props) || - * !equal(nextState, this.state) || - * !equal(nextContext, this.context); - * } - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @return {boolean} True if the component should update. - * @optional - */ - shouldComponentUpdate: 'DEFINE_ONCE', - - /** - * Invoked when the component is about to update due to a transition from - * \`this.props\`, \`this.state\` and \`this.context\` to \`nextProps\`, \`nextState\` - * and \`nextContext\`. - * - * Use this as an opportunity to perform preparation before an update occurs. - * - * NOTE: You **cannot** use \`this.setState()\` in this method. - * - * @param {object} nextProps - * @param {?object} nextState - * @param {?object} nextContext - * @param {ReactReconcileTransaction} transaction - * @optional - */ - componentWillUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component's DOM representation has been updated. - * - * Use this as an opportunity to operate on the DOM when the component has - * been updated. - * - * @param {object} prevProps - * @param {?object} prevState - * @param {?object} prevContext - * @param {DOMElement} rootNode DOM element representing the component. - * @optional - */ - componentDidUpdate: 'DEFINE_MANY', - - /** - * Invoked when the component is about to be removed from its parent and have - * its DOM representation destroyed. - * - * Use this as an opportunity to deallocate any external resources. - * - * NOTE: There is no \`componentDidUnmount\` since your component will have been - * destroyed by that point. - * - * @optional - */ - componentWillUnmount: 'DEFINE_MANY', - - // ==== Advanced methods ==== - - /** - * Updates the component's currently mounted DOM representation. - * - * By default, this implements React's rendering and reconciliation algorithm. - * Sophisticated clients may wish to override this. - * - * @param {ReactReconcileTransaction} transaction - * @internal - * @overridable - */ - updateComponent: 'OVERRIDE_BASE' - }; - - /** - * Mapping from class specification keys to special processing functions. - * - * Although these are declared like instance properties in the specification - * when defining classes using \`React.createClass\`, they are actually static - * and are accessible on the constructor instead of the prototype. Despite - * being static, they must be defined outside of the "statics" key under - * which all other static methods are defined. - */ - var RESERVED_SPEC_KEYS = { - displayName: function(Constructor, displayName) { - Constructor.displayName = displayName; - }, - mixins: function(Constructor, mixins) { - if (mixins) { - for (var i = 0; i < mixins.length; i++) { - mixSpecIntoComponent(Constructor, mixins[i]); - } - } - }, - childContextTypes: function(Constructor, childContextTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, childContextTypes, 'childContext'); - // } - Constructor.childContextTypes = _assign( - {}, - Constructor.childContextTypes, - childContextTypes - ); - }, - contextTypes: function(Constructor, contextTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, contextTypes, 'context'); - // } - Constructor.contextTypes = _assign( - {}, - Constructor.contextTypes, - contextTypes - ); - }, - /** - * Special case getDefaultProps which should move into statics but requires - * automatic merging. - */ - getDefaultProps: function(Constructor, getDefaultProps) { - if (Constructor.getDefaultProps) { - Constructor.getDefaultProps = createMergedResultFunction( - Constructor.getDefaultProps, - getDefaultProps - ); - } else { - Constructor.getDefaultProps = getDefaultProps; - } - }, - propTypes: function(Constructor, propTypes) { - // if (process.env.NODE_ENV !== 'production') { - // validateTypeDef(Constructor, propTypes, 'prop'); - // } - Constructor.propTypes = _assign({}, Constructor.propTypes, propTypes); - }, - statics: function(Constructor, statics) { - mixStaticSpecIntoComponent(Constructor, statics); - }, - autobind: function() {} - }; - - function validateTypeDef(Constructor, typeDef, location) { - for (var propName in typeDef) { - // if (typeDef.hasOwnProperty(propName)) { - // // use a warning instead of an _invariant so components - // // don't show up in prod but only in __DEV__ - // // if (process.env.NODE_ENV !== 'production') { - // // warning( - // // typeof typeDef[propName] === 'function', - // // '%s: %s type \`%s\` is invalid; it must be a function, usually from ' + - // // 'React.PropTypes.', - // // Constructor.displayName || 'ReactClass', - // // ReactPropTypeLocationNames[location], - // // propName - // // ); - // // } - // } - } - } - - function validateMethodOverride(isAlreadyDefined, name) { - var specPolicy = ReactClassInterface.hasOwnProperty(name) - ? ReactClassInterface[name] - : null; - - // Disallow overriding of base class methods unless explicitly allowed. - if (ReactClassMixin.hasOwnProperty(name)) { - // _invariant( - // specPolicy === 'OVERRIDE_BASE', - // 'ReactClassInterface: You are attempting to override ' + - // '\`%s\` from your class specification. Ensure that your method names ' + - // 'do not overlap with React methods.', - // name - // ); - } - - // Disallow defining methods more than once unless explicitly allowed. - if (isAlreadyDefined) { - // _invariant( - // specPolicy === 'DEFINE_MANY' || specPolicy === 'DEFINE_MANY_MERGED', - // 'ReactClassInterface: You are attempting to define ' + - // '\`%s\` on your component more than once. This conflict may be due ' + - // 'to a mixin.', - // name - // ); - } - } - - /** - * Mixin helper which handles policy validation and reserved - * specification keys when building React classes. - */ - function mixSpecIntoComponent(Constructor, spec) { - if (!spec) { - // if (process.env.NODE_ENV !== 'production') { - // var typeofSpec = typeof spec; - // var isMixinValid = typeofSpec === 'object' && spec !== null; - // - // if (process.env.NODE_ENV !== 'production') { - // warning( - // isMixinValid, - // "%s: You're attempting to include a mixin that is either null " + - // 'or not an object. Check the mixins included by the component, ' + - // 'as well as any mixins they include themselves. ' + - // 'Expected object but got %s.', - // Constructor.displayName || 'ReactClass', - // spec === null ? null : typeofSpec - // ); - // } - // } - - return; - } - - // _invariant( - // typeof spec !== 'function', - // "ReactClass: You're attempting to " + - // 'use a component class or function as a mixin. Instead, just use a ' + - // 'regular object.' - // ); - // _invariant( - // !isValidElement(spec), - // "ReactClass: You're attempting to " + - // 'use a component as a mixin. Instead, just use a regular object.' - // ); - - var proto = Constructor.prototype; - var autoBindPairs = proto.__reactAutoBindPairs; - - // By handling mixins before any other properties, we ensure the same - // chaining order is applied to methods with DEFINE_MANY policy, whether - // mixins are listed before or after these methods in the spec. - if (spec.hasOwnProperty(MIXINS_KEY)) { - RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins); - } - - for (var name in spec) { - if (!spec.hasOwnProperty(name)) { - continue; - } - - if (name === MIXINS_KEY) { - // We have already handled mixins in a special case above. - continue; - } - - var property = spec[name]; - var isAlreadyDefined = proto.hasOwnProperty(name); - validateMethodOverride(isAlreadyDefined, name); - - if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { - RESERVED_SPEC_KEYS[name](Constructor, property); - } else { - // Setup methods on prototype: - // The following member methods should not be automatically bound: - // 1. Expected ReactClass methods (in the "interface"). - // 2. Overridden methods (that were mixed in). - var isReactClassMethod = ReactClassInterface.hasOwnProperty(name); - var isFunction = typeof property === 'function'; - var shouldAutoBind = - isFunction && - !isReactClassMethod && - !isAlreadyDefined && - spec.autobind !== false; - - if (shouldAutoBind) { - autoBindPairs.push(name, property); - proto[name] = property; - } else { - if (isAlreadyDefined) { - var specPolicy = ReactClassInterface[name]; - - // These cases should already be caught by validateMethodOverride. - // _invariant( - // isReactClassMethod && - // (specPolicy === 'DEFINE_MANY_MERGED' || - // specPolicy === 'DEFINE_MANY'), - // 'ReactClass: Unexpected spec policy %s for key %s ' + - // 'when mixing in component specs.', - // specPolicy, - // name - // ); - - // For methods which are defined more than once, call the existing - // methods before calling the new property, merging if appropriate. - if (specPolicy === 'DEFINE_MANY_MERGED') { - proto[name] = createMergedResultFunction(proto[name], property); - } else if (specPolicy === 'DEFINE_MANY') { - proto[name] = createChainedFunction(proto[name], property); - } - } else { - proto[name] = property; - // if (process.env.NODE_ENV !== 'production') { - // // Add verbose displayName to the function, which helps when looking - // // at profiling tools. - // if (typeof property === 'function' && spec.displayName) { - // proto[name].displayName = spec.displayName + '_' + name; - // } - // } - } - } - } - } - } - - function mixStaticSpecIntoComponent(Constructor, statics) { - if (!statics) { - return; - } - for (var name in statics) { - var property = statics[name]; - if (!statics.hasOwnProperty(name)) { - continue; - } - - var isReserved = name in RESERVED_SPEC_KEYS; - // _invariant( - // !isReserved, - // 'ReactClass: You are attempting to define a reserved ' + - // 'property, \`%s\`, that shouldn\\'t be on the "statics" key. Define it ' + - // 'as an instance property instead; it will still be accessible on the ' + - // 'constructor.', - // name - // ); - - var isInherited = name in Constructor; - // _invariant( - // !isInherited, - // 'ReactClass: You are attempting to define ' + - // '\`%s\` on your component more than once. This conflict may be ' + - // 'due to a mixin.', - // name - // ); - Constructor[name] = property; - } - } - - /** - * Merge two objects, but throw if both contain the same key. - * - * @param {object} one The first object, which is mutated. - * @param {object} two The second object - * @return {object} one after it has been mutated to contain everything in two. - */ - function mergeIntoWithNoDuplicateKeys(one, two) { - // _invariant( - // one && two && typeof one === 'object' && typeof two === 'object', - // 'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.' - // ); - - for (var key in two) { - if (two.hasOwnProperty(key)) { - // _invariant( - // one[key] === undefined, - // 'mergeIntoWithNoDuplicateKeys(): ' + - // 'Tried to merge two objects with the same key: \`%s\`. This conflict ' + - // 'may be due to a mixin; in particular, this may be caused by two ' + - // 'getInitialState() or getDefaultProps() methods returning objects ' + - // 'with clashing keys.', - // key - // ); - one[key] = two[key]; - } - } - return one; - } - - /** - * Creates a function that invokes two functions and merges their return values. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ - function createMergedResultFunction(one, two) { - return function mergedResult() { - var a = one.apply(this, arguments); - var b = two.apply(this, arguments); - if (a == null) { - return b; - } else if (b == null) { - return a; - } - var c = {}; - mergeIntoWithNoDuplicateKeys(c, a); - mergeIntoWithNoDuplicateKeys(c, b); - return c; - }; - } - - /** - * Creates a function that invokes two functions and ignores their return vales. - * - * @param {function} one Function to invoke first. - * @param {function} two Function to invoke second. - * @return {function} Function that invokes the two argument functions. - * @private - */ - function createChainedFunction(one, two) { - return function chainedFunction() { - one.apply(this, arguments); - two.apply(this, arguments); - }; - } - - /** - * Binds a method to the component. - * - * @param {object} component Component whose method is going to be bound. - * @param {function} method Method to be bound. - * @return {function} The bound method. - */ - function bindAutoBindMethod(component, method) { - var boundMethod = method.bind(component); - // if (process.env.NODE_ENV !== 'production') { - // boundMethod.__reactBoundContext = component; - // boundMethod.__reactBoundMethod = method; - // boundMethod.__reactBoundArguments = null; - // var componentName = component.constructor.displayName; - // var _bind = boundMethod.bind; - // boundMethod.bind = function(newThis) { - // for ( - // var _len = arguments.length, - // args = Array(_len > 1 ? _len - 1 : 0), - // _key = 1; - // _key < _len; - // _key++ - // ) { - // args[_key - 1] = arguments[_key]; - // } - // - // // User is trying to bind() an autobound method; we effectively will - // // ignore the value of "this" that the user is trying to use, so - // // let's warn. - // if (newThis !== component && newThis !== null) { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // false, - // 'bind(): React component methods may only be bound to the ' + - // 'component instance. See %s', - // componentName - // ); - // } - // } else if (!args.length) { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // false, - // 'bind(): You are binding a component method to the component. ' + - // 'React does this for you automatically in a high-performance ' + - // 'way, so you can safely remove this call. See %s', - // componentName - // ); - // } - // return boundMethod; - // } - // var reboundMethod = _bind.apply(boundMethod, arguments); - // reboundMethod.__reactBoundContext = component; - // reboundMethod.__reactBoundMethod = method; - // reboundMethod.__reactBoundArguments = args; - // return reboundMethod; - // }; - // } - return boundMethod; - } - - /** - * Binds all auto-bound methods in a component. - * - * @param {object} component Component whose method is going to be bound. - */ - function bindAutoBindMethods(component) { - var pairs = component.__reactAutoBindPairs; - for (var i = 0; i < pairs.length; i += 2) { - var autoBindKey = pairs[i]; - var method = pairs[i + 1]; - component[autoBindKey] = bindAutoBindMethod(component, method); - } - } - - var IsMountedPreMixin = { - componentDidMount: function() { - this.__isMounted = true; - } - }; - - var IsMountedPostMixin = { - componentWillUnmount: function() { - this.__isMounted = false; - } - }; - - /** - * Add more to the ReactClass base class. These are all legacy features and - * therefore not already part of the modern ReactComponent. - */ - var ReactClassMixin = { - /** - * TODO: This will be deprecated because state should always keep a consistent - * type signature and the only use case for this, is to avoid that. - */ - replaceState: function(newState, callback) { - this.updater.enqueueReplaceState(this, newState, callback); - }, - - /** - * Checks whether or not this composite component is mounted. - * @return {boolean} True if mounted, false otherwise. - * @protected - * @final - */ - isMounted: function() { - // if (process.env.NODE_ENV !== 'production') { - // warning( - // this.__didWarnIsMounted, - // '%s: isMounted is deprecated. Instead, make sure to clean up ' + - // 'subscriptions and pending requests in componentWillUnmount to ' + - // 'prevent memory leaks.', - // (this.constructor && this.constructor.displayName) || - // this.name || - // 'Component' - // ); - // this.__didWarnIsMounted = true; - // } - return !!this.__isMounted; - } - }; - - var ReactClassComponent = function() {}; - _assign( - ReactClassComponent.prototype, - ReactComponent.prototype, - ReactClassMixin - ); - - /** - * Creates a composite component class given a class specification. - * See https://facebook.github.io/react/docs/top-level-api.html#react.createclass - * - * @param {object} spec Class specification (which must define \`render\`). - * @return {function} Component constructor function. - * @public - */ - function createClass(spec) { - // To keep our warnings more understandable, we'll use a little hack here to - // ensure that Constructor.name !== 'Constructor'. This makes sure we don't - // unnecessarily identify a class without displayName as 'Constructor'. - var Constructor = identity(function(props, context, updater) { - // This constructor gets overridden by mocks. The argument is used - // by mocks to assert on what gets mounted. - - // if (process.env.NODE_ENV !== 'production') { - // warning( - // this instanceof Constructor, - // 'Something is calling a React component directly. Use a factory or ' + - // 'JSX instead. See: https://fb.me/react-legacyfactory' - // ); - // } - - // Wire up auto-binding - if (this.__reactAutoBindPairs.length) { - bindAutoBindMethods(this); - } - - this.props = props; - this.context = context; - this.refs = emptyObject; - this.updater = updater || ReactNoopUpdateQueue; - - this.state = null; - - // ReactClasses doesn't have constructors. Instead, they use the - // getInitialState and componentWillMount methods for initialization. - - var initialState = this.getInitialState ? this.getInitialState() : null; - // if (process.env.NODE_ENV !== 'production') { - // // We allow auto-mocks to proceed as if they're returning null. - // if ( - // initialState === undefined && - // this.getInitialState._isMockFunction - // ) { - // // This is probably bad practice. Consider warning here and - // // deprecating this convenience. - // initialState = null; - // } - // } - // _invariant( - // typeof initialState === 'object' && !Array.isArray(initialState), - // '%s.getInitialState(): must return an object or null', - // Constructor.displayName || 'ReactCompositeComponent' - // ); - - this.state = initialState; - }); - Constructor.prototype = new ReactClassComponent(); - Constructor.prototype.constructor = Constructor; - Constructor.prototype.__reactAutoBindPairs = []; - - injectedMixins.forEach(mixSpecIntoComponent.bind(null, Constructor)); - - mixSpecIntoComponent(Constructor, IsMountedPreMixin); - mixSpecIntoComponent(Constructor, spec); - mixSpecIntoComponent(Constructor, IsMountedPostMixin); - - // Initialize the defaultProps property after all mixins have been merged. - if (Constructor.getDefaultProps) { - Constructor.defaultProps = Constructor.getDefaultProps(); - } - - // if (process.env.NODE_ENV !== 'production') { - // // This is a tag to indicate that the use of these method names is ok, - // // since it's used with createClass. If it's not, then it's likely a - // // mistake so we'll warn you to use the static property, property - // // initializer or constructor respectively. - // if (Constructor.getDefaultProps) { - // Constructor.getDefaultProps.isReactClassApproved = {}; - // } - // if (Constructor.prototype.getInitialState) { - // Constructor.prototype.getInitialState.isReactClassApproved = {}; - // } - // } - - // _invariant( - // Constructor.prototype.render, - // 'createClass(...): Class specification must implement a \`render\` method.' - // ); - - // if (process.env.NODE_ENV !== 'production') { - // warning( - // !Constructor.prototype.componentShouldUpdate, - // '%s has a method called ' + - // 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + - // 'The name is phrased as a question because the function is ' + - // 'expected to return a value.', - // spec.displayName || 'A component' - // ); - // warning( - // !Constructor.prototype.componentWillRecieveProps, - // '%s has a method called ' + - // 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', - // spec.displayName || 'A component' - // ); - // } - - // Reduce time spent doing lookups by setting these on the prototype. - for (var methodName in ReactClassInterface) { - if (!Constructor.prototype[methodName]) { - Constructor.prototype[methodName] = null; - } - } - - return Constructor; - } - - return createClass; -} -`) - -@module("react") external reactComponent: 'a = "Component" - -@module("react") external reactIsValidElement: bool = "isValidElement" - -@module("react") @new -external newReactComponent: unit => {"updater": 'a} = "Component" - -let reactNoopUpdateQueue = newReactComponent()["updater"] - -let createClass = factory(reactComponent, reactIsValidElement, reactNoopUpdateQueue) diff --git a/tests/tests/src/reasonReactRouter.mjs b/tests/tests/src/reasonReactRouter.mjs deleted file mode 100644 index 1e22e6d9b2..0000000000 --- a/tests/tests/src/reasonReactRouter.mjs +++ /dev/null @@ -1,184 +0,0 @@ -// Generated by ReScript, PLEASE EDIT WITH CARE - -import * as React from "react"; -import * as Js_string from "rescript/lib/es6/Js_string.js"; -import * as Primitive_option from "rescript/lib/es6/Primitive_option.js"; - -function safeMakeEvent(eventName) { - if (typeof Event === "function") { - return new Event(eventName); - } - let event = document.createEvent("Event"); - event.initEvent(eventName, true, true); - return event; -} - -function path() { - let window = globalThis.window; - if (window === undefined) { - return /* [] */0; - } - let raw = Primitive_option.valFromOption(window).location.pathname; - switch (raw) { - case "" : - case "/" : - return /* [] */0; - default: - let raw$1 = Js_string.sliceToEnd(1, raw); - let match = raw$1[raw$1.length - 1 | 0]; - let raw$2 = match === "/" ? Js_string.slice(0, -1, raw$1) : raw$1; - let a = Js_string.split("/", raw$2); - let _i = a.length - 1 | 0; - let _res = /* [] */0; - while (true) { - let res = _res; - let i = _i; - if (i < 0) { - return res; - } - _res = { - hd: a[i], - tl: res - }; - _i = i - 1 | 0; - continue; - }; - } -} - -function hash() { - let window = globalThis.window; - if (window === undefined) { - return ""; - } - let raw = Primitive_option.valFromOption(window).location.hash; - switch (raw) { - case "" : - case "#" : - return ""; - default: - return Js_string.sliceToEnd(1, raw); - } -} - -function search() { - let window = globalThis.window; - if (window === undefined) { - return ""; - } - let raw = Primitive_option.valFromOption(window).location.search; - switch (raw) { - case "" : - case "?" : - return ""; - default: - return Js_string.sliceToEnd(1, raw); - } -} - -function push(path) { - let match = globalThis.history; - let match$1 = globalThis.window; - if (match !== undefined && match$1 !== undefined) { - Primitive_option.valFromOption(match).pushState(null, "", path); - Primitive_option.valFromOption(match$1).dispatchEvent(safeMakeEvent("popstate")); - return; - } - -} - -function replace(path) { - let match = globalThis.history; - let match$1 = globalThis.window; - if (match !== undefined && match$1 !== undefined) { - Primitive_option.valFromOption(match).replaceState(null, "", path); - Primitive_option.valFromOption(match$1).dispatchEvent(safeMakeEvent("popstate")); - return; - } - -} - -function urlNotEqual(a, b) { - if (a.hash !== b.hash || a.search !== b.search) { - return true; - } else { - let _aList = a.path; - let _bList = b.path; - while (true) { - let bList = _bList; - let aList = _aList; - if (aList === 0) { - return bList !== 0; - } - if (bList === 0) { - return true; - } - if (aList.hd !== bList.hd) { - return true; - } - _bList = bList.tl; - _aList = aList.tl; - continue; - }; - } -} - -function url() { - return { - path: path(), - hash: hash(), - search: search() - }; -} - -function watchUrl(callback) { - let window = globalThis.window; - if (window === undefined) { - return () => {}; - } - let watcherID = () => callback(url()); - Primitive_option.valFromOption(window).addEventListener("popstate", watcherID); - return watcherID; -} - -function unwatchUrl(watcherID) { - let window = globalThis.window; - if (window !== undefined) { - Primitive_option.valFromOption(window).removeEventListener("popstate", watcherID); - return; - } - -} - -function useUrl(serverUrl, param) { - let match = React.useState(() => { - if (serverUrl !== undefined) { - return serverUrl; - } else { - return url(); - } - }); - let setUrl = match[1]; - let url$1 = match[0]; - React.useEffect(() => { - let watcherId = watchUrl(url => setUrl(param => url)); - let newUrl = url(); - if (urlNotEqual(newUrl, url$1)) { - setUrl(param => newUrl); - } - return () => unwatchUrl(watcherId); - }, []); - return url$1; -} - -let dangerouslyGetInitialUrl = url; - -export { - push, - replace, - watchUrl, - unwatchUrl, - dangerouslyGetInitialUrl, - useUrl, -} -/* react Not a pure module */ diff --git a/tests/tools_tests/package.json b/tests/tools_tests/package.json index f1f0ea9a57..b8f4b43ab6 100644 --- a/tests/tools_tests/package.json +++ b/tests/tools_tests/package.json @@ -7,7 +7,7 @@ "dev": "rescript -w" }, "dependencies": { - "@rescript/react": "link:../dependencies/rescript-react", + "@rescript/react": "workspace:^", "rescript": "workspace:^" } } diff --git a/yarn.config.cjs b/yarn.config.cjs index 42e6c4ac34..e7b77f7e19 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -15,7 +15,10 @@ async function enforceCompilerMeta({ Yarn }) { for (const workspace of Yarn.workspaces()) { const { ident } = workspace.pkg; - if (ident === "rescript" || ident.startsWith("@rescript/")) { + if ( + workspace.cwd.startsWith("packages") && + (ident === "rescript" || ident.startsWith("@rescript/")) + ) { workspace.set("version", EXPECTED_VERSION); workspace.set("homepage", "https://rescript-lang.org"); workspace.set("bugs", "https://github.com/rescript-lang/rescript/issues"); diff --git a/yarn.lock b/yarn.lock index 13a6d70b5f..8375e291c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -412,40 +412,20 @@ __metadata: languageName: unknown linkType: soft -"@rescript/react@link:../../../dependencies/rescript-react::locator=%40tests%2Freanalyze-deadcode%40workspace%3Atests%2Fanalysis_tests%2Ftests-reanalyze%2Fdeadcode": - version: 0.0.0-use.local - resolution: "@rescript/react@link:../../../dependencies/rescript-react::locator=%40tests%2Freanalyze-deadcode%40workspace%3Atests%2Fanalysis_tests%2Ftests-reanalyze%2Fdeadcode" - languageName: node - linkType: soft - -"@rescript/react@link:../../dependencies/rescript-react::locator=%40tests%2Fanalysis%40workspace%3Atests%2Fanalysis_tests%2Ftests": - version: 0.0.0-use.local - resolution: "@rescript/react@link:../../dependencies/rescript-react::locator=%40tests%2Fanalysis%40workspace%3Atests%2Fanalysis_tests%2Ftests" - languageName: node - linkType: soft - "@rescript/react@link:../dependencies/rescript-react::locator=%40tests%2Fdocstring-tests%40workspace%3Atests%2Fdocstring_tests": version: 0.0.0-use.local resolution: "@rescript/react@link:../dependencies/rescript-react::locator=%40tests%2Fdocstring-tests%40workspace%3Atests%2Fdocstring_tests" languageName: node linkType: soft -"@rescript/react@link:../dependencies/rescript-react::locator=%40tests%2Ftools%40workspace%3Atests%2Ftools_tests": +"@rescript/react@workspace:^, @rescript/react@workspace:tests/dependencies/rescript-react": version: 0.0.0-use.local - resolution: "@rescript/react@link:../dependencies/rescript-react::locator=%40tests%2Ftools%40workspace%3Atests%2Ftools_tests" - languageName: node + resolution: "@rescript/react@workspace:tests/dependencies/rescript-react" + dependencies: + rescript: "workspace:^" + languageName: unknown linkType: soft -"@rescript/react@npm:^0.13.1": - version: 0.13.1 - resolution: "@rescript/react@npm:0.13.1" - peerDependencies: - react: ">=18.0.0" - react-dom: ">=18.0.0" - checksum: 10c0/c4d04536daf7acc3f6452890665333211ad08723ecb9e9efdd66ea7bf7de6503f513d6dabad400415fcae7625740431f1c121fa3abbc4b7446b60e2e7052df32 - languageName: node - linkType: hard - "@rescript/std@workspace:packages/std": version: 0.0.0-use.local resolution: "@rescript/std@workspace:packages/std" @@ -636,7 +616,7 @@ __metadata: version: 0.0.0-use.local resolution: "@tests/analysis@workspace:tests/analysis_tests/tests" dependencies: - "@rescript/react": "link:../../dependencies/rescript-react" + "@rescript/react": "workspace:^" rescript: "workspace:^" languageName: unknown linkType: soft @@ -663,7 +643,7 @@ __metadata: resolution: "@tests/gentype-react-example@workspace:tests/gentype_tests/typescript-react-example" dependencies: "@biomejs/biome": "npm:1.9.4" - "@rescript/react": "npm:^0.13.1" + "@rescript/react": "workspace:^" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" react: "npm:^18.3.1" @@ -685,7 +665,7 @@ __metadata: version: 0.0.0-use.local resolution: "@tests/reanalyze-deadcode@workspace:tests/analysis_tests/tests-reanalyze/deadcode" dependencies: - "@rescript/react": "link:../../../dependencies/rescript-react" + "@rescript/react": "workspace:^" rescript: "workspace:^" languageName: unknown linkType: soft @@ -698,9 +678,12 @@ __metadata: languageName: unknown linkType: soft -"@tests/rescript-react@workspace:tests/dependencies/rescript-react": +"@tests/tests@workspace:tests/tests": version: 0.0.0-use.local - resolution: "@tests/rescript-react@workspace:tests/dependencies/rescript-react" + resolution: "@tests/tests@workspace:tests/tests" + dependencies: + "@rescript/react": "workspace:^" + rescript: "workspace:^" languageName: unknown linkType: soft @@ -708,7 +691,7 @@ __metadata: version: 0.0.0-use.local resolution: "@tests/tools@workspace:tests/tools_tests" dependencies: - "@rescript/react": "link:../dependencies/rescript-react" + "@rescript/react": "workspace:^" rescript: "workspace:^" languageName: unknown linkType: soft @@ -2407,7 +2390,7 @@ __metadata: version: 0.0.0-use.local resolution: "playground@workspace:packages/playground" dependencies: - "@rescript/react": "npm:^0.13.1" + "@rescript/react": "workspace:^" "@rollup/plugin-node-resolve": "npm:^16.0.0" glob: "npm:^11.0.1" rescript: "workspace:^"