From 183d9de0f0b9fdbeb37d0a4b8b6c63f61651bfb0 Mon Sep 17 00:00:00 2001 From: Josh Derocher-Vlk Date: Tue, 12 Aug 2025 09:45:53 -0400 Subject: [PATCH 1/6] Add prettier and format everything --- .github/ISSUE_TEMPLATE/documentation_issue.md | 8 +- .prettierrc | 3 + CHANGELOG.md | 6 +- README.md | 1 + .../2020-08-10-bucklescript-is-rebranding.mdx | 3 + _blogposts/2020-08-28-new-rescript-logo.mdx | 27 +- _blogposts/2020-09-25-release-8-3-2.mdx | 12 +- _blogposts/2020-09-25-release-8-3.mdx | 23 +- ...itor-support-custom-operators-and-more.mdx | 2 +- .../2020-11-26-editor-support-release-1-0.mdx | 8 +- _blogposts/2021-02-09-release-9-0.mdx | 10 +- _blogposts/2021-05-07-release-9-1.mdx | 18 +- _blogposts/2022-08-25-release-10-0-0.mdx | 7 + _blogposts/2023-02-02-release-10-1.mdx | 119 +-- _blogposts/2023-04-17-improving-interop.mdx | 12 +- ...-05-first-class-dynamic-import-support.mdx | 16 +- _blogposts/2023-09-18-uncurried-mode.mdx | 96 +-- _blogposts/2024-02-01-release-11-1-0.mdx | 64 +- ...2025-03-05-what-can-i-do-with-rescript.mdx | 43 +- _blogposts/2025-03-17-retreats.mdx | 5 +- ...25-04-11-introducing-unified-operators.mdx | 18 +- .../2017-10-06-bucklescript-release-1-5-2.mdx | 3 +- .../2017-10-07-bucklescript-release-1-7-0.mdx | 11 +- .../2017-10-08-bucklescript-release-1-7-4.mdx | 2 - .../2017-10-09-bucklescript-release-1-7-5.mdx | 5 +- ...18-03-13-a-small-step-for-bucklescript.mdx | 5 +- .../2018-04-16-bucklescript-release-3-0-0.mdx | 6 +- .../2018-05-21-bucklescript-release-3-1-0.mdx | 10 +- ...8-07-17-bucklescript-release-4-0-0-pt1.mdx | 3 +- ...8-07-17-bucklescript-release-4-0-0-pt2.mdx | 8 +- _blogposts/archive/2018-11-13-arity-zero.mdx | 12 +- ...8-11-19-bucklescript-roadmap-q3-4-2018.mdx | 2 - ...2019-01-07-bucklescript-release-4-0-17.mdx | 4 +- _blogposts/archive/2019-01-11-scalable.mdx | 54 +- .../2019-03-21-bucklescript-release-5-0.mdx | 3 +- .../2019-03-31-bucklescript-release-6-0.mdx | 2 +- .../2019-04-09-bucklescript-release-5-0-1.mdx | 4 +- .../2019-04-22-bucklescript-release-5-0-4.mdx | 1 - .../archive/2019-05-21-ffi-overview.mdx | 52 +- .../2019-06-26-bucklescript-release-5-0-5.mdx | 12 +- .../2019-08-12-bucklescript-release-5-1-0.mdx | 40 +- .../2019-09-23-bucklescript-release-5-2-0.mdx | 4 +- .../archive/2019-10-16-another-encoding.mdx | 8 +- .../archive/2019-11-18-whats-new-in-7-pt1.mdx | 13 +- .../archive/2019-11-28-whats-new-in-7-pt2.mdx | 6 +- .../2019-12-20-bucklescript-release-7-0-2.mdx | 13 +- .../2020-02-04-bucklescript-release-7-1-0.mdx | 233 +++--- ...2020-02-07-union-types-in-bucklescript.mdx | 12 +- .../2020-02-20-loading-stdlib-in-memory.mdx | 28 +- .../2020-03-12-bucklescript-release-7-2.mdx | 7 +- .../archive/2020-03-26-generalize-uncurry.mdx | 33 +- .../2020-04-13-bucklescript-release-7-3.mdx | 18 +- ...20-05-06-a-story-of-exception-encoding.mdx | 12 +- ...5-06-state-of-reasonml-org-2020-q2-pt1.mdx | 23 +- ...5-11-state-of-reasonml-org-2020-q2-pt2.mdx | 6 +- ...5-12-state-of-reasonml-org-2020-q2-pt3.mdx | 15 +- .../2020-05-15-a-story-of-lazy-encoding.mdx | 50 +- ...5-15-state-of-reasonml-org-2020-q2-pt4.mdx | 13 +- .../2020-06-22-overview-of-new_encoding.mdx | 62 +- ...2020-07-01-bucklescript-8-1-new-syntax.mdx | 1 + .../2020-07-17-bucklescript-release-8-1-1.mdx | 2 +- ...0-07-28-string-literal-types-in-reason.mdx | 31 +- .../2020-08-03-bucklescript-release-8-2.mdx | 2 - compilers/rescript.json | 11 +- data/api/v11.0.0/belt.json | 474 +++-------- data/api/v11.0.0/core.json | 114 +-- data/api/v11.0.0/dom.json | 2 +- data/api/v11.0.0/js.json | 474 +++-------- data/api/v11.0.0/toc_tree.json | 691 +++++++++++++++- data/api/v12.0.0/belt.json | 482 +++--------- data/api/v12.0.0/dom.json | 2 +- data/api/v12.0.0/js.json | 438 +++-------- data/api/v12.0.0/stdlib.json | 162 +--- data/api/v12.0.0/toc_tree.json | 740 +++++++++++++++++- data/resources.json | 112 ++- data/sidebar_community.json | 2 +- data/sidebar_manual_v1000.json | 15 +- data/sidebar_manual_v1100.json | 13 +- data/sidebar_manual_v1200.json | 20 +- data/sidebar_manual_v800.json | 11 +- data/sidebar_manual_v900.json | 10 +- data/sidebar_react_latest.json | 18 +- data/sidebar_react_v0100.json | 17 +- data/sidebar_react_v0110.json | 18 +- misc_docs/syntax/builtinfunctions_ignore.mdx | 10 +- misc_docs/syntax/decorator_as.mdx | 30 +- misc_docs/syntax/decorator_deriving.mdx | 15 +- misc_docs/syntax/decorator_directive.mdx | 5 +- .../decorator_expression_deprecated.mdx | 6 +- .../syntax/decorator_expression_warning.mdx | 6 +- misc_docs/syntax/decorator_gentype.mdx | 24 +- misc_docs/syntax/decorator_get.mdx | 2 +- misc_docs/syntax/decorator_get_index.mdx | 10 +- misc_docs/syntax/decorator_ignore.mdx | 5 +- misc_docs/syntax/decorator_inline.mdx | 5 +- misc_docs/syntax/decorator_int.mdx | 10 +- misc_docs/syntax/decorator_jsx_component.mdx | 11 +- misc_docs/syntax/decorator_meth.mdx | 17 +- misc_docs/syntax/decorator_module.mdx | 12 +- .../syntax/decorator_module_deprecated.mdx | 1 + misc_docs/syntax/decorator_module_warning.mdx | 3 +- misc_docs/syntax/decorator_new.mdx | 4 +- misc_docs/syntax/decorator_obj.mdx | 7 +- .../syntax/decorator_react_component.mdx | 13 +- .../decorator_react_component_with_props.mdx | 15 +- misc_docs/syntax/decorator_return.mdx | 8 +- misc_docs/syntax/decorator_scope.mdx | 4 +- misc_docs/syntax/decorator_send.mdx | 6 +- misc_docs/syntax/decorator_send_pipe.mdx | 7 +- misc_docs/syntax/decorator_set.mdx | 2 +- misc_docs/syntax/decorator_set_index.mdx | 10 +- misc_docs/syntax/decorator_string.mdx | 8 +- misc_docs/syntax/decorator_tag.mdx | 10 +- misc_docs/syntax/decorator_taggedTemplate.mdx | 11 +- misc_docs/syntax/decorator_this.mdx | 2 +- misc_docs/syntax/decorator_unboxed.mdx | 12 +- misc_docs/syntax/decorator_uncurried.mdx | 2 +- misc_docs/syntax/decorator_unwrap.mdx | 6 +- misc_docs/syntax/decorator_val.mdx | 2 +- misc_docs/syntax/decorator_variadic.mdx | 4 +- misc_docs/syntax/extension_debugger.mdx | 8 +- misc_docs/syntax/extension_identity.mdx | 11 +- misc_docs/syntax/extension_private_let.mdx | 11 +- misc_docs/syntax/extension_raw_expression.mdx | 8 +- .../extension_raw_top_level_expression.mdx | 14 +- .../syntax/extension_regular_expression.mdx | 10 +- misc_docs/syntax/extension_todo.mdx | 16 +- misc_docs/syntax/language_async.mdx | 8 +- misc_docs/syntax/language_await.mdx | 6 +- misc_docs/syntax/language_char_literal.mdx | 16 +- misc_docs/syntax/language_dict.mdx | 14 +- misc_docs/syntax/language_external.mdx | 17 +- misc_docs/syntax/language_for.mdx | 14 +- misc_docs/syntax/language_function.mdx | 4 +- misc_docs/syntax/language_if_else.mdx | 8 +- misc_docs/syntax/language_include.mdx | 17 +- .../syntax/language_labeled_argument.mdx | 18 +- misc_docs/syntax/language_let.mdx | 10 +- misc_docs/syntax/language_module.mdx | 10 +- misc_docs/syntax/language_open.mdx | 6 +- .../language_optional_labeled_argument.mdx | 28 +- misc_docs/syntax/language_placeholder.mdx | 15 +- misc_docs/syntax/language_polyvar.mdx | 6 +- .../syntax/language_regular_expression.mdx | 8 +- .../language_scoped_polymorphic_type.mdx | 8 +- misc_docs/syntax/language_spreads.mdx | 43 +- .../syntax/language_string_interpolation.mdx | 6 +- misc_docs/syntax/language_string_literal.mdx | 10 +- misc_docs/syntax/language_switch.mdx | 14 +- misc_docs/syntax/language_type.mdx | 15 +- misc_docs/syntax/language_type_parameter.mdx | 8 +- .../syntax/language_uncurried_function.mdx | 4 +- misc_docs/syntax/language_while.mdx | 16 +- .../syntax/operator_ref_value_assignment.mdx | 12 +- misc_docs/syntax/operators_float_addition.mdx | 6 +- misc_docs/syntax/operators_float_division.mdx | 6 +- .../syntax/operators_float_multiplication.mdx | 6 +- .../syntax/operators_float_subtraction.mdx | 6 +- .../syntax/operators_integer_addition.mdx | 6 +- .../syntax/operators_integer_division.mdx | 8 +- .../operators_integer_multiplication.mdx | 6 +- .../syntax/operators_integer_subtraction.mdx | 6 +- misc_docs/syntax/operators_mod.mdx | 6 +- misc_docs/syntax/operators_pipe.mdx | 22 +- .../syntax/operators_string_concatenation.mdx | 6 +- misc_docs/syntax/operators_triangle_pipe.mdx | 8 +- misc_docs/syntax/operators_type_coercion.mdx | 47 +- misc_docs/syntax/specialvalues_file.mdx | 4 +- misc_docs/syntax/specialvalues_line.mdx | 4 +- misc_docs/syntax/specialvalues_line_of.mdx | 14 +- misc_docs/syntax/specialvalues_loc.mdx | 4 +- misc_docs/syntax/specialvalues_loc_of.mdx | 14 +- misc_docs/syntax/specialvalues_module.mdx | 2 +- misc_docs/syntax/specialvalues_pos.mdx | 14 +- misc_docs/syntax/specialvalues_pos_of.mdx | 17 +- next.config.mjs | 84 +- package-lock.json | 379 ++++++++- package.json | 6 +- pages/_app.js | 19 +- pages/blog.js | 4 +- pages/blog/[slug].js | 6 +- pages/blog/archived.js | 4 +- pages/blogpost-guide.mdx | 9 +- pages/brand.mdx | 4 +- pages/community/content.mdx | 3 +- pages/community/translations.mdx | 9 +- pages/docs/manual/v10.0.0/api/belt/array.mdx | 4 +- pages/docs/manual/v10.0.0/api/belt/int.mdx | 1 - pages/docs/manual/v10.0.0/api/belt/list.mdx | 1 + pages/docs/manual/v10.0.0/api/belt/option.mdx | 2 +- pages/docs/manual/v10.0.0/api/belt/range.mdx | 2 +- .../docs/manual/v10.0.0/api/belt/set-dict.mdx | 1 - .../docs/manual/v10.0.0/api/belt/set-int.mdx | 1 - .../manual/v10.0.0/api/belt/set-string.mdx | 1 - pages/docs/manual/v10.0.0/api/dom.mdx | 6 +- pages/docs/manual/v10.0.0/api/js.mdx | 1 + pages/docs/manual/v10.0.0/api/js/array-2.mdx | 108 ++- pages/docs/manual/v10.0.0/api/js/array.mdx | 68 +- pages/docs/manual/v10.0.0/api/js/console.mdx | 1 - pages/docs/manual/v10.0.0/api/js/date.mdx | 158 +++- pages/docs/manual/v10.0.0/api/js/dict.mdx | 5 +- pages/docs/manual/v10.0.0/api/js/float.mdx | 5 +- pages/docs/manual/v10.0.0/api/js/json.mdx | 5 +- pages/docs/manual/v10.0.0/api/js/math.mdx | 38 +- pages/docs/manual/v10.0.0/api/js/option.mdx | 9 +- pages/docs/manual/v10.0.0/api/js/promise.mdx | 2 +- pages/docs/manual/v10.0.0/api/js/string-2.mdx | 12 +- pages/docs/manual/v10.0.0/api/js/string.mdx | 12 +- .../manual/v10.0.0/api/js/typed-array-2.mdx | 10 + .../api/js/typed-array-2_array-buffer.mdx | 1 - .../api/js/typed-array-2_float-32-array.mdx | 2 +- .../api/js/typed-array-2_float-64-array.mdx | 2 +- .../api/js/typed-array-2_int-16-array.mdx | 2 +- .../api/js/typed-array-2_int-32-array.mdx | 2 +- .../api/js/typed-array-2_int-8-array.mdx | 2 +- .../api/js/typed-array-2_uint-16-array.mdx | 2 +- .../api/js/typed-array-2_uint-32-array.mdx | 2 +- .../api/js/typed-array-2_uint-8-array.mdx | 2 +- .../js/typed-array-2_uint-8-clamped-array.mdx | 2 +- .../manual/v10.0.0/api/js/typed-array.mdx | 11 + .../api/js/typed-array_float-32-array.mdx | 2 +- .../api/js/typed-array_float-64-array.mdx | 2 +- .../api/js/typed-array_int-16-array.mdx | 2 +- .../api/js/typed-array_int-32-array.mdx | 2 +- .../api/js/typed-array_int-8-array.mdx | 2 +- .../api/js/typed-array_uint-16-array.mdx | 2 +- .../api/js/typed-array_uint-32-array.mdx | 2 +- .../api/js/typed-array_uint-8-array.mdx | 2 +- .../js/typed-array_uint-8-clamped-array.mdx | 2 +- pages/docs/manual/v10.0.0/array-and-list.mdx | 37 +- pages/docs/manual/v10.0.0/async-await.mdx | 43 +- pages/docs/manual/v10.0.0/attribute.mdx | 10 +- .../v10.0.0/bind-to-global-js-values.mdx | 26 +- .../manual/v10.0.0/bind-to-js-function.mdx | 93 ++- .../docs/manual/v10.0.0/bind-to-js-object.mdx | 38 +- .../v10.0.0/build-configuration-schema.mdx | 10 +- .../manual/v10.0.0/build-external-stdlib.mdx | 9 +- .../docs/manual/v10.0.0/build-performance.mdx | 8 +- .../v10.0.0/build-pinned-dependencies.mdx | 21 +- pages/docs/manual/v10.0.0/control-flow.mdx | 44 +- .../manual/v10.0.0/converting-from-js.mdx | 56 +- .../manual/v10.0.0/embed-raw-javascript.mdx | 22 +- pages/docs/manual/v10.0.0/exception.mdx | 97 +-- .../manual/v10.0.0/extensible-variant.mdx | 26 +- pages/docs/manual/v10.0.0/external.mdx | 13 +- pages/docs/manual/v10.0.0/faq.mdx | 9 +- pages/docs/manual/v10.0.0/function.mdx | 154 ++-- .../v10.0.0/generate-converters-accessors.mdx | 42 +- pages/docs/manual/v10.0.0/import-export.mdx | 3 +- .../v10.0.0/import-from-export-to-js.mdx | 48 +- .../manual/v10.0.0/inlining-constants.mdx | 12 +- .../manual/v10.0.0/interop-cheatsheet.mdx | 54 +- pages/docs/manual/v10.0.0/introduction.mdx | 1 - pages/docs/manual/v10.0.0/json.mdx | 11 +- pages/docs/manual/v10.0.0/jsx.mdx | 62 +- pages/docs/manual/v10.0.0/lazy-values.mdx | 37 +- pages/docs/manual/v10.0.0/let-binding.mdx | 46 +- .../migrate-from-bucklescript-reason.mdx | 1 - pages/docs/manual/v10.0.0/module.mdx | 101 ++- pages/docs/manual/v10.0.0/mutation.mdx | 14 +- .../docs/manual/v10.0.0/newcomer-examples.mdx | 22 +- .../manual/v10.0.0/null-undefined-option.mdx | 36 +- pages/docs/manual/v10.0.0/object.mdx | 37 +- pages/docs/manual/v10.0.0/overview.mdx | 170 ++-- .../pattern-matching-destructuring.mdx | 301 +++---- pages/docs/manual/v10.0.0/pipe.mdx | 72 +- .../manual/v10.0.0/polymorphic-variant.mdx | 64 +- pages/docs/manual/v10.0.0/primitive-types.mdx | 26 +- pages/docs/manual/v10.0.0/promise.mdx | 37 +- pages/docs/manual/v10.0.0/record.mdx | 106 ++- .../docs/manual/v10.0.0/reserved-keywords.mdx | 34 +- .../v10.0.0/scoped-polymorphic-types.mdx | 9 +- .../docs/manual/v10.0.0/shared-data-types.mdx | 2 + pages/docs/manual/v10.0.0/tuple.mdx | 28 +- pages/docs/manual/v10.0.0/type.mdx | 58 +- pages/docs/manual/v10.0.0/unboxed.mdx | 43 +- .../v10.0.0/use-illegal-identifier-names.mdx | 12 +- pages/docs/manual/v10.0.0/variant.mdx | 106 +-- pages/docs/manual/v10.0.0/warning-numbers.mdx | 2 +- pages/docs/manual/v11.0.0/api/[...slug].js | 10 +- pages/docs/manual/v11.0.0/array-and-list.mdx | 86 +- pages/docs/manual/v11.0.0/async-await.mdx | 43 +- pages/docs/manual/v11.0.0/attribute.mdx | 10 +- .../v11.0.0/bind-to-global-js-values.mdx | 26 +- .../manual/v11.0.0/bind-to-js-function.mdx | 106 +-- .../docs/manual/v11.0.0/bind-to-js-object.mdx | 38 +- .../v11.0.0/build-configuration-schema.mdx | 10 +- .../manual/v11.0.0/build-external-stdlib.mdx | 9 +- .../docs/manual/v11.0.0/build-performance.mdx | 2 +- pages/docs/manual/v11.0.0/control-flow.mdx | 44 +- .../manual/v11.0.0/converting-from-js.mdx | 56 +- .../manual/v11.0.0/embed-raw-javascript.mdx | 22 +- .../manual/v11.0.0/equality-comparison.mdx | 58 +- pages/docs/manual/v11.0.0/exception.mdx | 125 +-- .../manual/v11.0.0/extensible-variant.mdx | 26 +- pages/docs/manual/v11.0.0/external.mdx | 13 +- pages/docs/manual/v11.0.0/function.mdx | 156 ++-- .../v11.0.0/generate-converters-accessors.mdx | 27 +- pages/docs/manual/v11.0.0/import-export.mdx | 3 +- .../v11.0.0/import-from-export-to-js.mdx | 77 +- .../manual/v11.0.0/inlining-constants.mdx | 12 +- pages/docs/manual/v11.0.0/installation.mdx | 25 +- .../manual/v11.0.0/interop-cheatsheet.mdx | 56 +- pages/docs/manual/v11.0.0/introduction.mdx | 1 - pages/docs/manual/v11.0.0/json.mdx | 25 +- pages/docs/manual/v11.0.0/jsx.mdx | 92 ++- pages/docs/manual/v11.0.0/lazy-values.mdx | 37 +- pages/docs/manual/v11.0.0/let-binding.mdx | 46 +- .../docs/manual/v11.0.0/module-functions.mdx | 114 +-- pages/docs/manual/v11.0.0/module.mdx | 101 ++- pages/docs/manual/v11.0.0/mutation.mdx | 14 +- .../docs/manual/v11.0.0/newcomer-examples.mdx | 22 +- .../manual/v11.0.0/null-undefined-option.mdx | 36 +- pages/docs/manual/v11.0.0/object.mdx | 37 +- .../pattern-matching-destructuring.mdx | 303 +++---- pages/docs/manual/v11.0.0/pipe.mdx | 72 +- .../manual/v11.0.0/polymorphic-variant.mdx | 64 +- pages/docs/manual/v11.0.0/primitive-types.mdx | 56 +- pages/docs/manual/v11.0.0/promise.mdx | 37 +- pages/docs/manual/v11.0.0/record.mdx | 107 +-- .../v11.0.0/scoped-polymorphic-types.mdx | 9 +- .../docs/manual/v11.0.0/tagged-templates.mdx | 89 ++- pages/docs/manual/v11.0.0/tuple.mdx | 28 +- pages/docs/manual/v11.0.0/type.mdx | 58 +- .../manual/v11.0.0/typescript-integration.mdx | 47 +- .../v11.0.0/use-illegal-identifier-names.mdx | 12 +- pages/docs/manual/v11.0.0/variant.mdx | 177 +++-- pages/docs/manual/v11.0.0/warning-numbers.mdx | 2 +- pages/docs/manual/v12.0.0/api/[...slug].js | 10 +- pages/docs/manual/v12.0.0/array-and-list.mdx | 105 ++- pages/docs/manual/v12.0.0/async-await.mdx | 43 +- pages/docs/manual/v12.0.0/attribute.mdx | 10 +- .../v12.0.0/bind-to-global-js-values.mdx | 26 +- .../manual/v12.0.0/bind-to-js-function.mdx | 106 +-- .../docs/manual/v12.0.0/bind-to-js-object.mdx | 38 +- .../v12.0.0/build-configuration-schema.mdx | 10 +- .../manual/v12.0.0/build-external-stdlib.mdx | 9 +- .../docs/manual/v12.0.0/build-performance.mdx | 2 +- pages/docs/manual/v12.0.0/control-flow.mdx | 44 +- .../manual/v12.0.0/converting-from-js.mdx | 56 +- pages/docs/manual/v12.0.0/dict.mdx | 54 +- .../manual/v12.0.0/editor-code-analysis.mdx | 3 +- pages/docs/manual/v12.0.0/editor-plugins.mdx | 38 +- .../manual/v12.0.0/embed-raw-javascript.mdx | 22 +- .../manual/v12.0.0/equality-comparison.mdx | 58 +- pages/docs/manual/v12.0.0/exception.mdx | 157 ++-- .../manual/v12.0.0/extensible-variant.mdx | 26 +- pages/docs/manual/v12.0.0/external.mdx | 13 +- pages/docs/manual/v12.0.0/function.mdx | 156 ++-- .../v12.0.0/generate-converters-accessors.mdx | 27 +- pages/docs/manual/v12.0.0/import-export.mdx | 3 +- .../v12.0.0/import-from-export-to-js.mdx | 77 +- .../manual/v12.0.0/inlining-constants.mdx | 12 +- pages/docs/manual/v12.0.0/installation.mdx | 22 +- .../manual/v12.0.0/interop-cheatsheet.mdx | 56 +- pages/docs/manual/v12.0.0/introduction.mdx | 15 +- pages/docs/manual/v12.0.0/json.mdx | 25 +- pages/docs/manual/v12.0.0/jsx.mdx | 92 ++- pages/docs/manual/v12.0.0/lazy-values.mdx | 30 +- pages/docs/manual/v12.0.0/let-binding.mdx | 46 +- pages/docs/manual/v12.0.0/module.mdx | 101 ++- pages/docs/manual/v12.0.0/mutation.mdx | 14 +- .../manual/v12.0.0/null-undefined-option.mdx | 36 +- pages/docs/manual/v12.0.0/object.mdx | 37 +- pages/docs/manual/v12.0.0/overview.mdx | 20 +- .../pattern-matching-destructuring.mdx | 390 +++++---- pages/docs/manual/v12.0.0/pipe.mdx | 72 +- .../manual/v12.0.0/polymorphic-variant.mdx | 64 +- pages/docs/manual/v12.0.0/primitive-types.mdx | 56 +- pages/docs/manual/v12.0.0/promise.mdx | 37 +- pages/docs/manual/v12.0.0/record.mdx | 122 +-- .../v12.0.0/scoped-polymorphic-types.mdx | 9 +- .../docs/manual/v12.0.0/tagged-templates.mdx | 89 ++- pages/docs/manual/v12.0.0/tuple.mdx | 28 +- pages/docs/manual/v12.0.0/type.mdx | 58 +- .../manual/v12.0.0/typescript-integration.mdx | 47 +- .../v12.0.0/use-illegal-identifier-names.mdx | 12 +- pages/docs/manual/v12.0.0/variant.mdx | 193 +++-- pages/docs/manual/v12.0.0/warning-numbers.mdx | 2 +- pages/docs/manual/v8.0.0/api/belt.mdx | 6 +- pages/docs/manual/v8.0.0/api/belt/array.mdx | 2 +- pages/docs/manual/v8.0.0/api/belt/float.mdx | 6 +- .../manual/v8.0.0/api/belt/hash-map-int.mdx | 6 +- .../v8.0.0/api/belt/hash-map-string.mdx | 6 +- .../docs/manual/v8.0.0/api/belt/hash-map.mdx | 34 +- pages/docs/manual/v8.0.0/api/belt/int.mdx | 3 +- pages/docs/manual/v8.0.0/api/belt/list.mdx | 1 + pages/docs/manual/v8.0.0/api/belt/map.mdx | 4 +- pages/docs/manual/v8.0.0/api/belt/option.mdx | 2 +- pages/docs/manual/v8.0.0/api/belt/range.mdx | 2 +- pages/docs/manual/v8.0.0/api/dom.mdx | 6 +- pages/docs/manual/v8.0.0/api/js/array-2.mdx | 138 ++-- pages/docs/manual/v8.0.0/api/js/array.mdx | 82 +- pages/docs/manual/v8.0.0/api/js/console.mdx | 1 - pages/docs/manual/v8.0.0/api/js/date.mdx | 166 ++-- pages/docs/manual/v8.0.0/api/js/dict.mdx | 5 +- pages/docs/manual/v8.0.0/api/js/float.mdx | 7 +- pages/docs/manual/v8.0.0/api/js/json.mdx | 19 +- pages/docs/manual/v8.0.0/api/js/math.mdx | 40 +- pages/docs/manual/v8.0.0/api/js/option.mdx | 15 +- pages/docs/manual/v8.0.0/api/js/promise.mdx | 2 +- pages/docs/manual/v8.0.0/api/js/string-2.mdx | 30 +- pages/docs/manual/v8.0.0/api/js/string.mdx | 30 +- .../manual/v8.0.0/api/js/typed-array-2.mdx | 10 + .../api/js/typed-array-2_array-buffer.mdx | 1 - .../api/js/typed-array-2_float-32-array.mdx | 4 +- .../api/js/typed-array-2_float-64-array.mdx | 2 +- .../api/js/typed-array-2_int-16-array.mdx | 2 +- .../api/js/typed-array-2_int-32-array.mdx | 2 +- .../api/js/typed-array-2_int-8-array.mdx | 2 +- .../api/js/typed-array-2_uint-16-array.mdx | 2 +- .../api/js/typed-array-2_uint-32-array.mdx | 2 +- .../api/js/typed-array-2_uint-8-array.mdx | 2 +- .../js/typed-array-2_uint-8-clamped-array.mdx | 2 +- .../docs/manual/v8.0.0/api/js/typed-array.mdx | 13 +- .../api/js/typed-array_float-32-array.mdx | 2 +- .../api/js/typed-array_float-64-array.mdx | 2 +- .../api/js/typed-array_int-16-array.mdx | 2 +- .../api/js/typed-array_int-32-array.mdx | 2 +- .../v8.0.0/api/js/typed-array_int-8-array.mdx | 2 +- .../v8.0.0/api/js/typed-array_type-s.mdx | 1 - .../api/js/typed-array_uint-16-array.mdx | 2 +- .../api/js/typed-array_uint-32-array.mdx | 2 +- .../api/js/typed-array_uint-8-array.mdx | 2 +- .../js/typed-array_uint-8-clamped-array.mdx | 2 +- pages/docs/manual/v8.0.0/api/js/types.mdx | 1 + pages/docs/manual/v8.0.0/array-and-list.mdx | 42 +- .../v8.0.0/bind-to-global-js-values.mdx | 32 +- .../manual/v8.0.0/bind-to-js-function.mdx | 106 ++- .../docs/manual/v8.0.0/bind-to-js-object.mdx | 33 +- pages/docs/manual/v8.0.0/build-overview.mdx | 1 + .../docs/manual/v8.0.0/build-performance.mdx | 8 +- pages/docs/manual/v8.0.0/control-flow.mdx | 54 +- .../docs/manual/v8.0.0/converting-from-js.mdx | 62 +- .../manual/v8.0.0/embed-raw-javascript.mdx | 25 +- pages/docs/manual/v8.0.0/exception.mdx | 97 ++- pages/docs/manual/v8.0.0/external.mdx | 15 +- pages/docs/manual/v8.0.0/function.mdx | 137 ++-- .../v8.0.0/generate-converters-accessors.mdx | 63 +- pages/docs/manual/v8.0.0/import-export.mdx | 4 +- .../v8.0.0/import-from-export-to-js.mdx | 36 +- .../docs/manual/v8.0.0/interop-cheatsheet.mdx | 60 +- pages/docs/manual/v8.0.0/json.mdx | 13 +- pages/docs/manual/v8.0.0/jsx.mdx | 101 ++- pages/docs/manual/v8.0.0/lazy-values.mdx | 110 +-- pages/docs/manual/v8.0.0/let-binding.mdx | 63 +- pages/docs/manual/v8.0.0/module.mdx | 109 ++- pages/docs/manual/v8.0.0/mutation.mdx | 18 +- .../docs/manual/v8.0.0/newcomer-examples.mdx | 27 +- .../manual/v8.0.0/null-undefined-option.mdx | 50 +- pages/docs/manual/v8.0.0/object.mdx | 39 +- pages/docs/manual/v8.0.0/overview.mdx | 103 ++- .../v8.0.0/pattern-matching-destructuring.mdx | 245 +++--- pages/docs/manual/v8.0.0/pipe.mdx | 75 +- pages/docs/manual/v8.0.0/primitive-types.mdx | 22 +- pages/docs/manual/v8.0.0/promise.mdx | 24 +- pages/docs/manual/v8.0.0/record.mdx | 43 +- .../docs/manual/v8.0.0/shared-data-types.mdx | 2 + pages/docs/manual/v8.0.0/try.mdx | 2 + pages/docs/manual/v8.0.0/tuple.mdx | 33 +- pages/docs/manual/v8.0.0/type.mdx | 71 +- .../v8.0.0/use-illegal-identifier-names.mdx | 24 +- pages/docs/manual/v8.0.0/variant.mdx | 54 +- pages/docs/manual/v9.0.0/api/belt/array.mdx | 2 +- pages/docs/manual/v9.0.0/api/belt/int.mdx | 1 - pages/docs/manual/v9.0.0/api/belt/list.mdx | 1 + pages/docs/manual/v9.0.0/api/belt/option.mdx | 2 +- pages/docs/manual/v9.0.0/api/belt/range.mdx | 2 +- .../docs/manual/v9.0.0/api/belt/set-dict.mdx | 1 - pages/docs/manual/v9.0.0/api/belt/set-int.mdx | 1 - .../manual/v9.0.0/api/belt/set-string.mdx | 1 - pages/docs/manual/v9.0.0/api/dom.mdx | 6 +- pages/docs/manual/v9.0.0/api/js.mdx | 1 + pages/docs/manual/v9.0.0/api/js/array-2.mdx | 108 ++- pages/docs/manual/v9.0.0/api/js/array.mdx | 68 +- pages/docs/manual/v9.0.0/api/js/console.mdx | 1 - pages/docs/manual/v9.0.0/api/js/date.mdx | 158 +++- pages/docs/manual/v9.0.0/api/js/dict.mdx | 5 +- pages/docs/manual/v9.0.0/api/js/float.mdx | 5 +- pages/docs/manual/v9.0.0/api/js/json.mdx | 5 +- pages/docs/manual/v9.0.0/api/js/math.mdx | 38 +- pages/docs/manual/v9.0.0/api/js/option.mdx | 9 +- pages/docs/manual/v9.0.0/api/js/promise.mdx | 2 +- pages/docs/manual/v9.0.0/api/js/string-2.mdx | 12 +- pages/docs/manual/v9.0.0/api/js/string.mdx | 12 +- .../manual/v9.0.0/api/js/typed-array-2.mdx | 10 + .../api/js/typed-array-2_array-buffer.mdx | 1 - .../api/js/typed-array-2_float-32-array.mdx | 2 +- .../api/js/typed-array-2_float-64-array.mdx | 2 +- .../api/js/typed-array-2_int-16-array.mdx | 2 +- .../api/js/typed-array-2_int-32-array.mdx | 2 +- .../api/js/typed-array-2_int-8-array.mdx | 2 +- .../api/js/typed-array-2_uint-16-array.mdx | 2 +- .../api/js/typed-array-2_uint-32-array.mdx | 2 +- .../api/js/typed-array-2_uint-8-array.mdx | 2 +- .../js/typed-array-2_uint-8-clamped-array.mdx | 2 +- .../docs/manual/v9.0.0/api/js/typed-array.mdx | 11 + .../api/js/typed-array_float-32-array.mdx | 2 +- .../api/js/typed-array_float-64-array.mdx | 2 +- .../api/js/typed-array_int-16-array.mdx | 2 +- .../api/js/typed-array_int-32-array.mdx | 2 +- .../v9.0.0/api/js/typed-array_int-8-array.mdx | 2 +- .../api/js/typed-array_uint-16-array.mdx | 2 +- .../api/js/typed-array_uint-32-array.mdx | 2 +- .../api/js/typed-array_uint-8-array.mdx | 2 +- .../js/typed-array_uint-8-clamped-array.mdx | 2 +- pages/docs/manual/v9.0.0/array-and-list.mdx | 37 +- pages/docs/manual/v9.0.0/attribute.mdx | 8 +- .../v9.0.0/bind-to-global-js-values.mdx | 26 +- .../manual/v9.0.0/bind-to-js-function.mdx | 90 ++- .../docs/manual/v9.0.0/bind-to-js-object.mdx | 27 +- .../v9.0.0/build-configuration-schema.mdx | 10 +- .../manual/v9.0.0/build-external-stdlib.mdx | 9 +- pages/docs/manual/v9.0.0/build-overview.mdx | 3 +- .../docs/manual/v9.0.0/build-performance.mdx | 8 +- .../v9.0.0/build-pinned-dependencies.mdx | 23 +- pages/docs/manual/v9.0.0/control-flow.mdx | 44 +- .../docs/manual/v9.0.0/converting-from-js.mdx | 56 +- pages/docs/manual/v9.0.0/editor-plugins.mdx | 3 +- .../manual/v9.0.0/embed-raw-javascript.mdx | 22 +- pages/docs/manual/v9.0.0/exception.mdx | 90 ++- pages/docs/manual/v9.0.0/external.mdx | 13 +- pages/docs/manual/v9.0.0/faq.mdx | 9 +- pages/docs/manual/v9.0.0/function.mdx | 118 +-- .../v9.0.0/generate-converters-accessors.mdx | 42 +- pages/docs/manual/v9.0.0/import-export.mdx | 3 +- .../v9.0.0/import-from-export-to-js.mdx | 48 +- .../docs/manual/v9.0.0/inlining-constants.mdx | 12 +- pages/docs/manual/v9.0.0/installation.mdx | 1 + .../docs/manual/v9.0.0/interop-cheatsheet.mdx | 54 +- pages/docs/manual/v9.0.0/introduction.mdx | 1 - pages/docs/manual/v9.0.0/json.mdx | 11 +- pages/docs/manual/v9.0.0/jsx.mdx | 88 ++- pages/docs/manual/v9.0.0/lazy-values.mdx | 37 +- pages/docs/manual/v9.0.0/let-binding.mdx | 56 +- .../migrate-from-bucklescript-reason.mdx | 2 +- pages/docs/manual/v9.0.0/module.mdx | 93 ++- pages/docs/manual/v9.0.0/mutation.mdx | 14 +- .../docs/manual/v9.0.0/newcomer-examples.mdx | 22 +- .../manual/v9.0.0/null-undefined-option.mdx | 36 +- pages/docs/manual/v9.0.0/object.mdx | 32 +- pages/docs/manual/v9.0.0/overview.mdx | 141 ++-- .../v9.0.0/pattern-matching-destructuring.mdx | 299 +++---- pages/docs/manual/v9.0.0/pipe.mdx | 72 +- .../manual/v9.0.0/polymorphic-variant.mdx | 57 +- pages/docs/manual/v9.0.0/primitive-types.mdx | 22 +- pages/docs/manual/v9.0.0/promise.mdx | 24 +- pages/docs/manual/v9.0.0/record.mdx | 34 +- .../docs/manual/v9.0.0/reserved-keywords.mdx | 35 +- .../docs/manual/v9.0.0/shared-data-types.mdx | 2 + pages/docs/manual/v9.0.0/tuple.mdx | 28 +- pages/docs/manual/v9.0.0/type.mdx | 58 +- pages/docs/manual/v9.0.0/unboxed.mdx | 43 +- .../v9.0.0/use-illegal-identifier-names.mdx | 12 +- pages/docs/manual/v9.0.0/variant.mdx | 106 +-- pages/docs/react/latest/arrays-and-keys.mdx | 187 +++-- pages/docs/react/latest/beyond-jsx.mdx | 46 +- .../react/latest/components-and-props.mdx | 113 +-- pages/docs/react/latest/context.mdx | 91 ++- pages/docs/react/latest/elements-and-jsx.mdx | 45 +- .../docs/react/latest/extensions-of-props.mdx | 3 +- pages/docs/react/latest/forwarding-refs.mdx | 35 +- pages/docs/react/latest/hooks-context.mdx | 80 +- pages/docs/react/latest/hooks-custom.mdx | 358 +++++---- pages/docs/react/latest/hooks-effect.mdx | 123 +-- pages/docs/react/latest/hooks-overview.mdx | 50 +- pages/docs/react/latest/hooks-reducer.mdx | 255 +++--- pages/docs/react/latest/hooks-ref.mdx | 72 +- pages/docs/react/latest/hooks-state.mdx | 77 +- .../react/latest/import-export-reactjs.mdx | 54 +- pages/docs/react/latest/lazy-components.mdx | 2 +- pages/docs/react/latest/memo.mdx | 79 +- pages/docs/react/latest/refs-and-the-dom.mdx | 113 +-- .../docs/react/latest/rendering-elements.mdx | 8 +- pages/docs/react/latest/router.mdx | 53 +- pages/docs/react/latest/server-components.mdx | 83 +- pages/docs/react/v0.10.0/arrays-and-keys.mdx | 4 +- pages/docs/react/v0.10.0/beyond-jsx.mdx | 26 +- .../react/v0.10.0/components-and-props.mdx | 117 +-- pages/docs/react/v0.10.0/context.mdx | 101 ++- pages/docs/react/v0.10.0/elements-and-jsx.mdx | 44 +- pages/docs/react/v0.10.0/forwarding-refs.mdx | 87 +- pages/docs/react/v0.10.0/hooks-context.mdx | 90 +-- pages/docs/react/v0.10.0/hooks-custom.mdx | 358 +++++---- pages/docs/react/v0.10.0/hooks-effect.mdx | 123 +-- pages/docs/react/v0.10.0/hooks-overview.mdx | 50 +- pages/docs/react/v0.10.0/hooks-reducer.mdx | 255 +++--- pages/docs/react/v0.10.0/hooks-ref.mdx | 81 +- pages/docs/react/v0.10.0/hooks-state.mdx | 87 +- pages/docs/react/v0.10.0/refs-and-the-dom.mdx | 112 +-- .../docs/react/v0.10.0/rendering-elements.mdx | 8 +- pages/docs/react/v0.10.0/router.mdx | 53 +- pages/docs/react/v0.11.0/arrays-and-keys.mdx | 4 +- pages/docs/react/v0.11.0/beyond-jsx.mdx | 26 +- .../react/v0.11.0/components-and-props.mdx | 100 +-- pages/docs/react/v0.11.0/context.mdx | 91 ++- pages/docs/react/v0.11.0/elements-and-jsx.mdx | 47 +- .../react/v0.11.0/extensions-of-props.mdx | 7 +- pages/docs/react/v0.11.0/forwarding-refs.mdx | 35 +- pages/docs/react/v0.11.0/hooks-context.mdx | 80 +- pages/docs/react/v0.11.0/hooks-custom.mdx | 358 +++++---- pages/docs/react/v0.11.0/hooks-effect.mdx | 123 +-- pages/docs/react/v0.11.0/hooks-overview.mdx | 50 +- pages/docs/react/v0.11.0/hooks-reducer.mdx | 255 +++--- pages/docs/react/v0.11.0/hooks-ref.mdx | 81 +- pages/docs/react/v0.11.0/hooks-state.mdx | 87 +- pages/docs/react/v0.11.0/refs-and-the-dom.mdx | 112 +-- .../docs/react/v0.11.0/rendering-elements.mdx | 8 +- pages/docs/react/v0.11.0/router.mdx | 53 +- pages/docs/v10.0.0.js | 4 +- pages/docs/v11.0.0.js | 4 +- pages/docs/v12.0.0.js | 4 +- pages/docs/v8.0.0.js | 4 +- pages/docs/v9.0.0.js | 4 +- pages/markdown-guide.mdx | 47 +- pages/packages.js | 2 +- pages/syntax-lookup.js | 4 +- pages/try.js | 2 +- plugins/cm-reason-mode.js | 57 +- plugins/cm-rescript-mode.js | 65 +- plugins/reason-highlightjs.js | 228 +++--- public/static/docson/box.html | 239 +++--- public/static/docson/signature.html | 153 ++-- public/static/favicon/site.webmanifest | 20 +- rescript.json | 9 +- scripts/bsb-benchmark-test.js | 87 +- scripts/extract-indices.mjs | 132 ++-- scripts/extract-syntax.mjs | 75 +- scripts/extract-tocs.mjs | 182 ++--- scripts/figma-fetch.js | 192 +++-- scripts/markdown.js | 74 +- scripts/sync-playground-bundles.mjs | 62 +- scripts/sync-redirects.mjs | 22 +- scripts/test-examples.mjs | 206 +++-- scripts/test-hrefs.mjs | 280 +++---- src/ffi/loadScript.js | 24 +- src/ffi/parse-numeric-range.js | 22 +- src/ffi/react-codemirror-hooks.js | 148 ++-- styles/_docsearch.css | 24 +- styles/_fonts.css | 121 ++- styles/_hljs.css | 24 +- styles/_markdown.css | 15 +- styles/_theme.css | 12 +- styles/cm.css | 60 +- styles/docson.css | 460 +++++------ styles/main.css | 42 +- styles/utils.css | 8 +- tailwind.config.mjs | 162 ++-- 648 files changed, 16045 insertions(+), 12374 deletions(-) create mode 100644 .prettierrc diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.md b/.github/ISSUE_TEMPLATE/documentation_issue.md index f371b6270..95cb9bae2 100644 --- a/.github/ISSUE_TEMPLATE/documentation_issue.md +++ b/.github/ISSUE_TEMPLATE/documentation_issue.md @@ -1,9 +1,7 @@ --- name: 🚨 Documentation issue about: Create an issue to help us improve the rescript-lang.org documentation website -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- - diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..262c35dd2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +semi: false +plugins: + - "@prettier/plugin-oxc" diff --git a/CHANGELOG.md b/CHANGELOG.md index b84fef4c6..a46ed2ce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ # Changelog - This changelog documents significant changes that caused a new version in the manual, or other related resources. We don't create a version fork for every minor release, and try to make docs "append only" as much as possible. Usually when we introduce a new feature we add a `since 9.0` annotation to a specific section, and be done with it. @@ -13,8 +12,7 @@ Here are the notes on major changes we did, and how the version corresponds to s ### latest -- 9.1 related - - +- ## 9.1 related ### v9.0 (v8.3 - v9.0) @@ -34,5 +32,3 @@ Here are the notes on major changes we did, and how the version corresponds to s ### v8.0.0 (v6 - v8.3) - Docs with Reason / OCaml syntax before the new syntax - - diff --git a/README.md b/README.md index 90d521539..7837c53ad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # rescript-lang.org + [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) This is the official documentation platform for the [ReScript](https://rescript-lang.org) programming language. diff --git a/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx index 168c3abf5..fb0d54ca7 100644 --- a/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx +++ b/_blogposts/2020-08-10-bucklescript-is-rebranding.mdx @@ -21,6 +21,7 @@ We're pleased to announce that BuckleScript is getting a brand new name: **ReScr ## Community Situation BuckleScript started with the idea that **JavaScript programmers deserved a great typed language with a fast and lean toolchain**. This idea took root and, over the years, we've gradually accomplished feats such as: + - a state of the art compiled JavaScript output that rivals hand-written JS, - a fast & reliable toolchain much needed in front-end and Node development, - various JS interop features that spawned an ecosystem of well typed libraries, @@ -28,6 +29,7 @@ BuckleScript started with the idea that **JavaScript programmers deserved a grea - [and recently](/blog/bucklescript-8-1-new-syntax), a fresh syntax made by a major contributor of Reason's old syntax. These developments have attracted many people into our community. But one bigger challenge persisted: newcomers dropped out at the sheer amount of extra incongruent tools and learning overhead from having to understand OCaml concepts, Reason concepts, and BuckleScript's concepts. Take, for example, what's required to make a React app using BuckleScript: + - Knowledge of React. - Knowledge of JS. - Knowledge of BuckleScript's specific bindings to React (that we've tried hard to keep to a minimum). @@ -43,6 +45,7 @@ The adoption barrier is real, and it's about time we finally solve it. ## The Rebranding Today, we'll start to truly unify the various BuckleScript-related projects under the ReScript umbrella. This includes: + - The compiler, build system and the new syntax unified under a single installation. - Doubling down on editor tooling for ReScript usage. - A single documentation site (this one), which unifies all the docs and greatly trims down on redundant and stale info. diff --git a/_blogposts/2020-08-28-new-rescript-logo.mdx b/_blogposts/2020-08-28-new-rescript-logo.mdx index 0d40e3903..e193ee424 100644 --- a/_blogposts/2020-08-28-new-rescript-logo.mdx +++ b/_blogposts/2020-08-28-new-rescript-logo.mdx @@ -7,7 +7,7 @@ description: | Today, our resident designer Bettina is unveiling to us the fresh new ReScript branding we've been long waiting for. We hope you're as excited about the result as us! --- -import Image from "src/components/Image"; +import Image from "src/components/Image" ## Why the Rebranding? @@ -15,11 +15,14 @@ ReScript is the evolution and fusion of Reason and BuckleScript. As we're a desi Here it is! - - - + The old Reason and BuckleScript logo had many limitations: + - The icons didn't really work well on round Social Media profile images (or round shapes at all). - There's no guideline on how the logo works inverted, or on colored background. - The large red rectangle makes it difficult to balance the logo with other elements. @@ -27,15 +30,21 @@ The old Reason and BuckleScript logo had many limitations: The new logo addresses all these and more: - + ## Creating Meaning Through Simple Shapes A minimal logo design is achieved by putting aside all distractions while focusing on legibility, meaning and small details like clear lines, interesting perspective and depth. - - - + **The letter "r"**, composed of two shapes, is inspired by Albers, a german-born American Bauhaus artist and typographer. An object should be simple, beautiful, functional and accessible for everyone - a statement which applies to ReScript's principles perfectly. @@ -43,7 +52,7 @@ A minimal logo design is achieved by putting aside all distractions while focusi **The red app-shaped background** references the vibrant, playful applications our developers are empowered to create with ReScript. -**The Logotype** ("rescript") complements the brand mark with its clean look and its embrace of technology while retaining a functional look. +**The Logotype** ("rescript") complements the brand mark with its clean look and its embrace of technology while retaining a functional look. We'd like to position ReScript to be a community of product-first developers who care about the fit & finish of their work, while keeping an eye on the quality of their engineering. This interplay of design and engineering is a hard-to-achieve but beautifully worthwhile sweet spot that's frequently been missing in the programmer community; our vibrant logo is our renewed symbolic step toward this mission. Come along with us on our journey! diff --git a/_blogposts/2020-09-25-release-8-3-2.mdx b/_blogposts/2020-09-25-release-8-3-2.mdx index a0ba49a92..2ad89accb 100644 --- a/_blogposts/2020-09-25-release-8-3-2.mdx +++ b/_blogposts/2020-09-25-release-8-3-2.mdx @@ -74,7 +74,7 @@ To get the build output, instead of communicating through IPC, we adopted a simp This makes the editor integration build-system agnostic, it does not need talk to the build system directly. It also makes our build tool work with other watchers including Facebook's [watchman](https://facebook.github.io/watchman/). -Watchman is a more scalable watcher tool for some specific platforms and less memory hungry, however, we still need a watchman-client to get the output of triggered job. +Watchman is a more scalable watcher tool for some specific platforms and less memory hungry, however, we still need a watchman-client to get the output of triggered job. We write the output to `.compiler.log` per each build, allowing clients to read compiler diagnostics when they want. ## A better algorithm for removing stale outputs @@ -90,8 +90,8 @@ We scan `lib/bs` directory and check some dangling `cm{i,t,j,ti}` files, if it d the current build set, it is considered stale artifacts. If it is `cmt` file, it would trigger some hooks of `genType`, notably -cmt-rm. - Based on previous build logs -We store previous compilation stats. If a file is in the previous compiler output, but no longer in the output of the new build, it is considered stale and can be removed. -it is considered stale output which can be removed. + We store previous compilation stats. If a file is in the previous compiler output, but no longer in the output of the new build, it is considered stale and can be removed. + it is considered stale output which can be removed. In general, strategy two is more reliable and efficient. @@ -100,11 +100,9 @@ In general, strategy two is more reliable and efficient. However, strategy one is easier for tooling like `genType`. Not every tool has knowledge of the build system. - Sometimes a combination of both strategies is needed. -- When removing .cm* files, we use the first strategy. -- When removing generated javascript, we use strategy two, - +- When removing .cm\* files, we use the first strategy. +- When removing generated javascript, we use strategy two, Happy Hacking! diff --git a/_blogposts/2020-09-25-release-8-3.mdx b/_blogposts/2020-09-25-release-8-3.mdx index 5975e6678..af8cbf8b4 100644 --- a/_blogposts/2020-09-25-release-8-3.mdx +++ b/_blogposts/2020-09-25-release-8-3.mdx @@ -17,9 +17,7 @@ It's focused on type safety, performance and JS interop. It used to be called Bu npm i bs-platform@8.3.0 ``` -The changes are listed [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#83), this is a large release, and we will go through some highlighted changes. - - +The changes are listed [here](https://github.com/rescript-lang/rescript/blob/master/Changes.md#83), this is a large release, and we will go through some highlighted changes. ## Lightweight FFI attributes without `bs.` prefix @@ -27,18 +25,19 @@ In this release, we make the `bs.` prefix optional, this will make the FFI less For example, the old externals for `readFileAsUtf8Sync` used to be written like this - ```res @bs.val @bs.module("fs") external readFileAsUtf8Sync: (string, @bs.as("utf8") _) => string = "readFileSync" ``` + ```reason [@bs.val] [@bs.module "fs"] external readFileAsUtf8Sync: (string, [@bs.as "utf8"] _) => string = "readFileSync"; ``` + ```ocaml external readFileAsUtf8Sync : string -> (_[@bs.as "utf8"]) -> string = "readFileSync" [@@bs.val] [@@bs.module "fs"] ``` @@ -52,11 +51,13 @@ It can now be simplified as ```res @val @module("fs") external readFileAsUtf8Sync: (string, @as("utf8") _) => string = "readFileSync" ``` + ```reason [@val] [@module "fs"] external readFileAsUtf8Sync: (string, [@as "utf8"] _) => string = "readFileSync"; ``` + ```ocaml external readFileAsUtf8Sync : string -> (_[@as "utf8"]) -> string = "readFileSync" [@@val] [@@module "fs"] @@ -70,8 +71,7 @@ with the exception of the following two that don't have abbreviations: - `bs.send.pipe` : this attribute was deprecated in favor of `bs.send`; you can still use the existing one for backward compatibility. - `bs.splice` : this attribute was deprecated in favor of `bs.variadic`; you can still use the existing one for -backward compatibility. - + backward compatibility. ## default import in Es6 support @@ -84,11 +84,13 @@ If you use es6 module output, the default bindings will be compiled properly now let a = input("hello") ``` + ```reason [@module "hello"] external input: string => string = "default"; let a = input("hello"); ``` + ```ocaml external input : string -> string = "default" [@@module "hello"] @@ -100,11 +102,10 @@ let a = input "hello" Will now be compiled properly under es6 format as below: ```js -import Hello from "hello"; -var a = Hello("hello"); +import Hello from "hello" +var a = Hello("hello") ``` - ## Customized js file extension support Now user can pick up their js file extension support per module format: @@ -126,8 +127,6 @@ To have better integration with other [JS infrastructures](https://github.com/re for example, Next.js/React Native, we allow file names like `404.res`, `Button.Android.res` so that it can just be picked up by those tools - - ## Better type based inference for pattern `let {a,b,c} = value` Previously, for code like this: @@ -144,6 +143,7 @@ let f = (u: N.t) => { x + 1 } /* type error */ ``` + ```reason module N = { type t = {x: int}; @@ -154,6 +154,7 @@ let f = (u: N.t) => { x + 1; }; /* type error */ ``` + ```ocaml module N = struct type t = { diff --git a/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx b/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx index 43b21fe30..2427987a7 100644 --- a/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx +++ b/_blogposts/2020-11-17-editor-support-custom-operators-and-more.mdx @@ -8,7 +8,7 @@ description: | Update on what we're doing around the end of 2020 and early next year. --- -import Video from "src/components/Video"; +import Video from "src/components/Video" ## Upcoming Improvements diff --git a/_blogposts/2020-11-26-editor-support-release-1-0.mdx b/_blogposts/2020-11-26-editor-support-release-1-0.mdx index 090f101b6..08ae79ed0 100644 --- a/_blogposts/2020-11-26-editor-support-release-1-0.mdx +++ b/_blogposts/2020-11-26-editor-support-release-1-0.mdx @@ -9,12 +9,12 @@ description: | Type hints, jump to definition, error diagnostics, and more. --- - ## Editor Integration Has Finally Landed Thank you so much for the wait! **Here are all the features:** + - Highlighting - Formatting - Diagnostics @@ -32,4 +32,8 @@ We'll keep on iterating on the polish of the plugins, and release better [Sublim Happy thanksgiving! - + diff --git a/_blogposts/2021-02-09-release-9-0.mdx b/_blogposts/2021-02-09-release-9-0.mdx index 0ae1f049c..712e3df68 100644 --- a/_blogposts/2021-02-09-release-9-0.mdx +++ b/_blogposts/2021-02-09-release-9-0.mdx @@ -34,7 +34,6 @@ Our compiler comes with a set of stdlib modules (such as `Belt`, `Pervasives`, e In previous versions, users couldn't ship their compiled JS code without defining a `package.json` dependency on `bs-platform`. Whenever a ReScript developer wanted to publish a package just for pure JS consumption / lean container deployment, they were required to use a bundler to bundle up their library / stdlib code, which made things way more complex and harder to understand. - To fix this problem, we introduced an `external-stdlib` configuration that allows specifying a pre-compiled stdlib npm package (`@rescript/std`). More details on how to use that feature can be found in our [External Stdlib](/docs/manual/latest/build-external-stdlib) documentation. ### Less Bundle Bloat when Adding ReScript @@ -67,7 +66,7 @@ The snippet above will compile to the following JS output: ```js -function test(x){ +function test(x) { return x === 0 } ``` @@ -75,14 +74,13 @@ function test(x){ ```js function test(x) { if (typeof x === "number") { - return x === 0; + return x === 0 } else { - return false; + return false } } ``` - As you can see, the 9.0 compiler removes all the unnecessary `typeof` checks! @@ -153,7 +151,6 @@ type animation = [ #\"ease-in" | #\"ease-out" ] We introduced this change to allow easier interop with existing JS string enums. In pure ReScript code, we'd still recommend our users to stick with valid identifier names instead (e.g. `easeIn` instead of `ease-in`). - ## Breaking Changes This release comes with a minor breaking change that shouldn't have much impact on the upgrade of existing codebases. @@ -209,5 +206,4 @@ For example, we are tinkering with the idea on using WASM to replace Camlp4, and We will discuss these topics in a separate development post, but we are already excited about the new possibilities this will bring within the compiler toolchain. - Happy Hacking! diff --git a/_blogposts/2021-05-07-release-9-1.mdx b/_blogposts/2021-05-07-release-9-1.mdx index 75f021180..27fc85dbf 100644 --- a/_blogposts/2021-05-07-release-9-1.mdx +++ b/_blogposts/2021-05-07-release-9-1.mdx @@ -41,6 +41,7 @@ The default `rescript` is equivalent to `rescript build` subcommand ``` Here's a table of translation, if you're upgrading your script that is currently using `bsc` and `bsb`: + - `bsc -format myFile.res`: `rescript format myFile.res` - `bsb`: `rescript build` \* - `bsb -make-world`: `rescript build -with-deps` \* @@ -53,7 +54,8 @@ This means that you can ditch your old `-make-world` (now the explicit `-with-de ### Polymorphic Variants for Numbers and Strings -*Drumrolls* +_Drumrolls_ + - Poly variants like `#1`, `#42` compile to JavaScript numbers. - Poly variants like `#hello`, `#world` compile to JavaScript Strings. @@ -77,13 +79,13 @@ let test = (arg: t) => { ``` ```js -var secret = 42; +var secret = 42 function test(arg) { if (arg === 5) { - return "world"; + return "world" } else { - return "hello"; + return "hello" } } ``` @@ -109,15 +111,15 @@ Js.log(test3(Some(#3))) ```js function test2(arg) { - return arg; + return arg } function test3(arg) { - return arg; + return arg } -console.log(1); -console.log(3); +console.log(1) +console.log(3) ``` diff --git a/_blogposts/2022-08-25-release-10-0-0.mdx b/_blogposts/2022-08-25-release-10-0-0.mdx index b66493af2..9413fa21d 100644 --- a/_blogposts/2022-08-25-release-10-0-0.mdx +++ b/_blogposts/2022-08-25-release-10-0-0.mdx @@ -17,15 +17,19 @@ npm install rescript@10 All changes are listed [here](https://github.com/rescript-lang/rescript/blob/10.0_release/CHANGELOG.md). Let's take a tour of a few of the features we're extra excited about. ## Faster builds with native M1 support + Users with M1 chips should see a notable speedup, as the new ReScript version has full native support for M1. ## Better ergonomics with Unicode support in regular strings + You can now use Unicode characters directly in regular strings. This will now produce what you'd expect: + ```res let str = "Σ" ``` You can also pattern match on Unicode characters: + ```res switch someCharacter { | 'Σ' => "what a fine Unicode char" @@ -34,7 +38,9 @@ switch someCharacter { ``` ## Experimental optional record fields + Previously, a record would always have to define all its optional fields: + ```res type user = { name: string, @@ -51,6 +57,7 @@ let userWithAge = { age: Some(34), } ``` + For small records like the one above, this is typically fine. But for records with many fields, the friction of having to always set all optional fields explicitly adds up. This release has a new experimental feature called optional record fields, allowing you to rewrite the above to this instead: ```res diff --git a/_blogposts/2023-02-02-release-10-1.mdx b/_blogposts/2023-02-02-release-10-1.mdx index cce653a2e..930782a6a 100644 --- a/_blogposts/2023-02-02-release-10-1.mdx +++ b/_blogposts/2023-02-02-release-10-1.mdx @@ -70,11 +70,10 @@ Way easier on the eyes, don't you think? Note that the new `promise` type is ful Additionally, we also introduced the `Js.Promise2` module as a stepping stone to migrate `Js.Promise` based code to a first-pipe (->) friendly solution. For the daily practise you'll almost always want to use `async` / `await` to handle promises. -(*Sidenote*: We are also well aware that our users want a solution to unify `Belt`, `Js` and `Js.xxx2` and have a fully featured "standard library" instead of adding more `Js.xxx2` modules. Good news is that we have a solution in the pipeline to fix this. `Js.Promise2` was introduced to ease the process later on and is not supposed to be the panacea of promise handling.) +(_Sidenote_: We are also well aware that our users want a solution to unify `Belt`, `Js` and `Js.xxx2` and have a fully featured "standard library" instead of adding more `Js.xxx2` modules. Good news is that we have a solution in the pipeline to fix this. `Js.Promise2` was introduced to ease the process later on and is not supposed to be the panacea of promise handling.) If you are already using a third-party promise library like [ryyppy/rescript-promise](https://github.com/ryyppy/rescript-promise) or similar, there's no need to migrate any existing code. Introduce `async` / `await` gradually in your codebase as you go. - ## New JSX v4 syntax ReScript 10.1 now ships with JSX v4. Here's what's new: @@ -83,7 +82,7 @@ ReScript 10.1 now ships with JSX v4. Here's what's new: - **Two new transformation modes**. JSX v4 comes with a `classic` mode (= `React.createElement`) and `automatic` mode (= `jsx-runtime` calls). The latter is the new default, moving forward with `rescript/react@0.11` and `React@18`. - **Allow mixing JSX configurations on the project and module level.** Gradually mix and match JSX transformations and modes without migrating any old code! - **Pass `prop` types** to `@react.component`. You can now fine tune `@react.component` with your specific prop type needs. Very useful for libraries and frameworks to define component interfaces. -- **Less boilerplate when using `React.Context`**. Check out our [example](/docs/react/latest/migrate-react#reactcontext) for comparison. +- **Less boilerplate when using `React.Context`**. Check out our [example](/docs/react/latest/migrate-react#reactcontext) for comparison. - **Revisited props spread operator.** This will allow users to spread records in JSX without sacrificing their sanity. Note that this implementation has harder constraints than its JS counterpart. (requires `rescript/react@0.11` or higher) - **Better type inference of props.** Type inference when passing e.g. variants that are defined in the same module as the component is much improved. With the earlier JSX version, you'd often need to write code like this in order for the compiler to understand which variant you're passing: ` ``` + ```js use client' // Generated by ReScript, PLEASE EDIT WITH CARE diff --git a/pages/docs/react/v0.10.0/arrays-and-keys.mdx b/pages/docs/react/v0.10.0/arrays-and-keys.mdx index 56be3f290..7543924ac 100644 --- a/pages/docs/react/v0.10.0/arrays-and-keys.mdx +++ b/pages/docs/react/v0.10.0/arrays-and-keys.mdx @@ -77,7 +77,7 @@ module Blog = {

{React.string(post.content)}

}); - +
{sidebar}
@@ -94,7 +94,6 @@ let posts = [ let blog = ``` - ## Rendering `list` Values In case you ever want to render a `list` of items, you can do something like this: @@ -124,4 +123,3 @@ let make = () => { We use `Belt.List.toArray` to convert our list to an array before creating our `array`. Please note that using `list` has performance impact due to extra conversion costs. 99% of the time you'll want to use arrays (seamless interop, faster JS code), but in some cases it might make sense to use a `list` to leverage advanced pattern matching features etc. - diff --git a/pages/docs/react/v0.10.0/beyond-jsx.mdx b/pages/docs/react/v0.10.0/beyond-jsx.mdx index b12aeba0b..08cd18f63 100644 --- a/pages/docs/react/v0.10.0/beyond-jsx.mdx +++ b/pages/docs/react/v0.10.0/beyond-jsx.mdx @@ -14,11 +14,11 @@ JSX is a syntax sugar that allows us to use React components in an HTML like man **Note:** This section requires knowledge about the low level apis for [creating elements](./elements-and-jsx#creating-elements-from-component-functions), such as `React.createElement` or `ReactDOMRe.createDOMElementVariadic`. -> **Note:** This page assumes your `bsconfig.json` to be set to `"reason": { "react-jsx": 3 }` to apply the right JSX transformations. +> **Note:** This page assumes your `bsconfig.json` to be set to `"reason": { "react-jsx": 3 }` to apply the right JSX transformations. ## Component Types -A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component<'props>`. +A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component<'props>`. Here are some examples on how to define your own component types (often useful when interoping with existing JS code, or passing around components): @@ -35,6 +35,7 @@ type containerComp = "children": React.element }>; ``` + The types above are pretty low level (basically the JS representation of a React component), but since ReScript React has its own ways of defining React components in a more language specific way, let's have a closer look on the anatomy of such a construct. ## JSX Component Interface @@ -54,6 +55,7 @@ module Friend = { } } ``` + ```res module Friend = { @obj @@ -82,7 +84,6 @@ In the expanded output: The `@react.component` decorator also works for `React.forwardRef` calls: - ```res @@ -129,18 +130,20 @@ So now that we know how the ReScript React component transformation works, let's ## JSX Under the Hood -Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children: +Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children: ```res ``` + ```res React.createElement(Friend.make, Friend.makeProps(~name="Fred", ~age=1, ())) ``` + ```js -React.createElement(Playground$Friend, { name: "Fred", age: 20 }); +React.createElement(Playground$Friend, { name: "Fred", age: 20 }) ``` @@ -165,17 +168,16 @@ React.createElementVariadic( ``` ```js -React.createElement(Container, { width: 200, children: null }, "Hello", "World"); +React.createElement(Container, { width: 200, children: null }, "Hello", "World") ``` Note that the `~children=React.null` prop has no relevance since React will only care about the children array passed as a third argument. - ### Dom Elements -"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOMRe.createDOMElementVariadic` calls: +"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOMRe.createDOMElementVariadic` calls: @@ -188,7 +190,7 @@ ReactDOMRe.createDOMElementVariadic("div", ~props=ReactDOMRe.domProps(~title="te ``` ```js - React.createElement("div", { title: "test" }); +React.createElement("div", { title: "test" }) ``` @@ -212,7 +214,11 @@ ReactDOMRe.createDOMElementVariadic( ``` ```js -React.createElement("div", { title: "test" }, React.createElement("span", undefined)); +React.createElement( + "div", + { title: "test" }, + React.createElement("span", undefined), +) ``` diff --git a/pages/docs/react/v0.10.0/components-and-props.mdx b/pages/docs/react/v0.10.0/components-and-props.mdx index 160eb515b..568e64767 100644 --- a/pages/docs/react/v0.10.0/components-and-props.mdx +++ b/pages/docs/react/v0.10.0/components-and-props.mdx @@ -8,13 +8,13 @@ canonical: "/docs/react/latest/components-and-props" -Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. +Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. ## What is a Component? -A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`. +A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`. The nice thing about this concept is that you can solely focus on the input and output. The component function receives some data and returns some opaque `React.element` that is managed by the React framework to render your UI. @@ -35,14 +35,15 @@ let make = () => {
} ``` + ```js -var React = require("react"); +var React = require("react") function Greeting(Props) { - return React.createElement("div", undefined, "Hello ReScripters!"); + return React.createElement("div", undefined, "Hello ReScripters!") } -var make = Greeting; +var make = Greeting ``` @@ -51,7 +52,7 @@ var make = Greeting; We've created a `Greeting.res` file that contains a `make` function that doesn't receive any props (the function doesn't receive any parameters), and returns a `React.element` that represents `
Hello ReScripters!
` in the rendered DOM. -You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `React.createElement("div",...)` call in JavaScript. +You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `React.createElement("div",...)` call in JavaScript. ## Defining Props @@ -71,18 +72,25 @@ let make = (~title: string, ~visitorCount: int, ~children: React.element) => {
} ``` + ```js -var React = require("react"); +var React = require("react") function Article(Props) { - var title = Props.title; - var visitorCount = Props.visitorCount; - var children = Props.children; - var visitorCountMsg = "You are visitor number: " + String(visitorCount); - return React.createElement("div", undefined, React.createElement("div", undefined, title), React.createElement("div", undefined, visitorCountMsg), children); + var title = Props.title + var visitorCount = Props.visitorCount + var children = Props.children + var visitorCountMsg = "You are visitor number: " + String(visitorCount) + return React.createElement( + "div", + undefined, + React.createElement("div", undefined, title), + React.createElement("div", undefined, visitorCountMsg), + children, + ) } -var make = Article; +var make = Article ``` @@ -107,15 +115,15 @@ let make = (~name: option=?) => { ```js function Greeting(Props) { - var name = Props.name; - var greeting = name !== undefined ? "Hello " + name + "!" : "Hello stranger!"; - return React.createElement("div", undefined, greeting); + var name = Props.name + var greeting = name !== undefined ? "Hello " + name + "!" : "Hello stranger!" + return React.createElement("div", undefined, greeting) } ``` -**Note:** The `@react.component` attribute implicitly adds the last `()` parameter to our `make` function for us (no need to do it ourselves). +**Note:** The `@react.component` attribute implicitly adds the last `()` parameter to our `make` function for us (no need to do it ourselves). In JSX, you can apply optional props with some special syntax: @@ -128,16 +136,16 @@ let name = Some("Andrea") ``` ```js -var Caml_option = require("./stdlib/caml_option.js"); -var name = "Andrea"; +var Caml_option = require("./stdlib/caml_option.js") +var name = "Andrea" -var tmp = {}; +var tmp = {} if (name !== undefined) { - tmp.name = Caml_option.valFromOption(name); + tmp.name = Caml_option.valFromOption(name) } -var greeting = React.createElement(Playground$Greeting, tmp); +var greeting = React.createElement(Playground$Greeting, tmp) ``` @@ -150,13 +158,12 @@ Check out the corresponding [Arrays and Keys](./arrays-and-keys) and [Forwarding ### Handling Invalid Prop Names (e.g. keywords) -Prop names like `type` (as in ``) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `` instead. +Prop names like `type` (as in ``) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `` instead. For `aria-*` use camelCasing, e.g., `ariaLabel`. For DOM components, we'll translate it to `aria-label` under the hood. For `data-*` this is a bit trickier; words with `-` in them aren't valid in ReScript. When you do want to write them, e.g., `
`, check out the [React.cloneElement](./elements-and-jsx#cloning-elements) or [React.createDOMElementVariadic](./elements-and-jsx#creating-dom-elements) section. - ## Children Props In React `props.children` is a special attribute to represent the nested elements within a parent element: @@ -187,20 +194,23 @@ module MyList = { ``` ```js - function MyList(Props) { - var children = Props.children; - return React.createElement("ul", undefined, children); + var children = Props.children + return React.createElement("ul", undefined, children) } var MyList = { - make: MyList -}; + make: MyList, +} -React.createElement(MyList, { - children: null - }, React.createElement("li", undefined, "Item 1"), - React.createElement("li", undefined, "Item 2")); +React.createElement( + MyList, + { + children: null, + }, + React.createElement("li", undefined, "Item 1"), + React.createElement("li", undefined, "Item 2"), +) ``` @@ -270,14 +280,12 @@ The best way to approach this kind of issue is by using props instead of childre **The best use-case for `children` is to pass down `React.element`s without any semantic order or implementation details!** - ## Props & Type Inference -The ReScript type system is really good at inferring the prop types just by looking at its prop usage. +The ReScript type system is really good at inferring the prop types just by looking at its prop usage. For simple cases, well-scoped usage, or experimentation, it's still fine to omit type annotations: - ```res // Button.res @@ -310,22 +318,26 @@ let make = () => {
} ``` + ```js -var React = require("react"); +var React = require("react") var Greeting = require("./Greeting.js") function App(Props) { - return React.createElement("div", undefined, React.createElement(Greeting.make, {})); + return React.createElement( + "div", + undefined, + React.createElement(Greeting.make, {}), + ) } -var make = App; +var make = App ``` **Note:** React components are capitalized; primitive DOM elements like `div` or `button` are uncapitalized. More infos on the JSX specifics and code transformations can be found in our [JSX language manual section](/docs/manual/latest/jsx#capitalized-tag). - ### Handwritten Components You don't need to use the `@react.component` decorator to write components that can be used in JSX. Instead you can write a pair of `make` and `makeProps` functions such that type `makeProps: 'a => props` and `make: props => React.element` and these will always work as React components. @@ -342,7 +354,7 @@ module Link = { ~children: React.element, unit) => props = "" - let make = (props: props) => { + let make = (props: props) => { {props["children"]} @@ -351,27 +363,31 @@ module Link = { {React.string("Docs")} ``` + ```js function Link(props) { - return React.createElement("a", { - href: props.href - }, props.children); + return React.createElement( + "a", + { + href: props.href, + }, + props.children, + ) } React.createElement(Link, { - href: "/docs", - children: "Docs" - }); + href: "/docs", + children: "Docs", +}) ``` More details on the `@react.component` decorator and its generated interface can be found in our [Beyond JSX](./beyond-jsx) page. - ## Submodule Components -We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): +We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): ```res // src/Button.res @@ -393,7 +409,6 @@ let make = (~children) => { The `Button.res` file defined above is now containing a `Label` component, that can also be used by other components, either by writing the fully qualified module name (``) or by using a module alias to shortcut the full qualifier: - ```res module Label = Button.Label @@ -404,7 +419,6 @@ let content =
} ``` + ```js function Counter(Props) { var match = React.useState(function () { - return 0; - }); - var setCount = match[1]; + return 0 + }) + var setCount = match[1] var onClick = function (_evt) { - return Curry._1(setCount, (function (prev) { - return prev + 1 | 0; - })); - }; - var msg = "You clicked" + String(match[0]) + "times"; - return React.createElement("div", undefined, React.createElement("p", undefined, msg), React.createElement("button", { - onClick: onClick - }, "Click me")); + return Curry._1(setCount, function (prev) { + return (prev + 1) | 0 + }) + } + var msg = "You clicked" + String(match[0]) + "times" + return React.createElement( + "div", + undefined, + React.createElement("p", undefined, msg), + React.createElement( + "button", + { + onClick: onClick, + }, + "Click me", + ), + ) } ``` - -Here we are using the `React.useState` Hook. We call it inside a component function to add some local state to it. React will preserve this state between re-renders. `React.useState` returns a tuple: the current state value (`count`) and a function that lets you update it (`setCount`). You can call this function from an event handler or pass it down to other components to call the function. +Here we are using the `React.useState` Hook. We call it inside a component function to add some local state to it. React will preserve this state between re-renders. `React.useState` returns a tuple: the current state value (`count`) and a function that lets you update it (`setCount`). You can call this function from an event handler or pass it down to other components to call the function. The only argument to `React.useState` is a function that returns the initial state (`_ => 0`). In the example above, it is 0 because our counter starts from zero. Note that your state can be any type you want and `ReScript` will make sure to infer the types for you (only make sure to return an initial state that matches your type). The initial state argument is only used during the first render. @@ -84,12 +93,11 @@ This was just a quick example on our first hook usage. We will go into more deta ### Additional Hooks: - [useReducer](./hooks-reducer): An alternative to `useState`. Uses the state / action / reduce pattern. - - + + - [useRef](./hooks-ref): Returns a mutable React-Ref value - - - + + ## Rules of Hooks @@ -97,10 +105,8 @@ Hooks are just simple functions, but you need to follow _two rules_ when using t ### Rule 1) Only Call Hooks at the Top Level - **Don’t call Hooks inside loops, conditions, or nested functions.** Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple `useState` and `useEffect` calls. (If you’re curious, you can check out the in depth explanation in the [ReactJS Hooks docs](https://reactjs.org/docs/hooks-rules.html#explanation)) - ### Rule 2) Only Call Hooks from React Functions **Don't call Hooks from regular functions.** Instead, you can: diff --git a/pages/docs/react/v0.10.0/hooks-reducer.mdx b/pages/docs/react/v0.10.0/hooks-reducer.mdx index 885ac59ce..ed4e61583 100644 --- a/pages/docs/react/v0.10.0/hooks-reducer.mdx +++ b/pages/docs/react/v0.10.0/hooks-reducer.mdx @@ -21,12 +21,12 @@ let (state, dispatch) = React.useReducer(reducer, initialState) ``` ```js -var match = React.useReducer(reducer, initialState); +var match = React.useReducer(reducer, initialState) ``` -An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, action) => newState`, and returns the current `state` paired with a `dispatch` function `(action) => unit`. +An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, action) => newState`, and returns the current `state` paired with a `dispatch` function `(action) => unit`. `React.useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks. @@ -67,29 +67,43 @@ let make = () => { function reducer(state, action) { if (action) { return { - count: state.count - 1 | 0 - }; + count: (state.count - 1) | 0, + } } else { return { - count: state.count + 1 | 0 - }; + count: (state.count + 1) | 0, + } } } function Counter(Props) { var match = React.useReducer(reducer, { - count: 0 - }); - var dispatch = match[1]; - return React.createElement(React.Fragment, undefined, "Count:" + String(match[0].count), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Dec */1); - }) - }, "-"), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Inc */0); - }) - }, "+")); + count: 0, + }) + var dispatch = match[1] + return React.createElement( + React.Fragment, + undefined, + "Count:" + String(match[0].count), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Dec */ 1) + }, + }, + "-", + ), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Inc */ 0) + }, + }, + "+", + ), + ) } ``` @@ -177,88 +191,103 @@ let make = () => { ```js function reducer(state, action) { switch (action.TAG | 0) { - case /* AddTodo */0 : - var todos = state.todos.concat([{ - id: state.nextId, - content: action._0, - completed: false - }]); - return { - todos: todos, - nextId: state.nextId + 1 | 0 - }; - case /* RemoveTodo */1 : - var id = action._0; - var todos$1 = state.todos.filter(function (todo) { - return todo.id !== id; - }); - return { - todos: todos$1, - nextId: state.nextId - }; - case /* ToggleTodo */2 : - var id$1 = action._0; - var todos$2 = Belt_Array.map(state.todos, (function (todo) { - if (todo.id === id$1) { - return { - id: todo.id, - content: todo.content, - completed: !todo.completed - }; - } else { - return todo; - } - })); - return { - todos: todos$2, - nextId: state.nextId - }; - + case /* AddTodo */ 0: + var todos = state.todos.concat([ + { + id: state.nextId, + content: action._0, + completed: false, + }, + ]) + return { + todos: todos, + nextId: (state.nextId + 1) | 0, + } + case /* RemoveTodo */ 1: + var id = action._0 + var todos$1 = state.todos.filter(function (todo) { + return todo.id !== id + }) + return { + todos: todos$1, + nextId: state.nextId, + } + case /* ToggleTodo */ 2: + var id$1 = action._0 + var todos$2 = Belt_Array.map(state.todos, function (todo) { + if (todo.id === id$1) { + return { + id: todo.id, + content: todo.content, + completed: !todo.completed, + } + } else { + return todo + } + }) + return { + todos: todos$2, + nextId: state.nextId, + } } } -var initialTodos = [{ +var initialTodos = [ + { id: 1, content: "Try ReScript & React", - completed: false - }]; + completed: false, + }, +] function TodoApp(Props) { var match = React.useReducer(reducer, { - todos: initialTodos, - nextId: 2 - }); - var dispatch = match[1]; - var todos = Belt_Array.map(match[0].todos, (function (todo) { - return React.createElement("li", undefined, todo.content, React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, { - TAG: /* RemoveTodo */1, - _0: todo.id - }); - }) - }, "Remove"), React.createElement("input", { - checked: todo.completed, - type: "checkbox", - onChange: (function (param) { - return Curry._1(dispatch, { - TAG: /* ToggleTodo */2, - _0: todo.id - }); - }) - })); - })); - return React.createElement(React.Fragment, undefined, React.createElement("h1", undefined, "Todo List:"), React.createElement("ul", undefined, todos)); + todos: initialTodos, + nextId: 2, + }) + var dispatch = match[1] + var todos = Belt_Array.map(match[0].todos, function (todo) { + return React.createElement( + "li", + undefined, + todo.content, + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, { + TAG: /* RemoveTodo */ 1, + _0: todo.id, + }) + }, + }, + "Remove", + ), + React.createElement("input", { + checked: todo.completed, + type: "checkbox", + onChange: function (param) { + return Curry._1(dispatch, { + TAG: /* ToggleTodo */ 2, + _0: todo.id, + }) + }, + }), + ) + }) + return React.createElement( + React.Fragment, + undefined, + React.createElement("h1", undefined, "Todo List:"), + React.createElement("ul", undefined, todos), + ) } ``` - ## Lazy Initialization - - ```res @@ -267,7 +296,7 @@ let (state, dispatch) = ``` ```js -var match = React.useReducer(reducer, initialState, init); +var match = React.useReducer(reducer, initialState, init) ``` @@ -315,41 +344,55 @@ let make = (~initialCount: int) => { ```js function init(initialCount) { return { - count: initialCount - }; + count: initialCount, + } } function reducer(state, action) { if (typeof action === "number") { if (action !== 0) { return { - count: state.count - 1 | 0 - }; + count: (state.count - 1) | 0, + } } else { return { - count: state.count + 1 | 0 - }; + count: (state.count + 1) | 0, + } } } else { return { - count: action._0 - }; + count: action._0, + } } } function Counter(Props) { - var initialCount = Props.initialCount; - var match = React.useReducer(reducer, initialCount, init); - var dispatch = match[1]; - return React.createElement(React.Fragment, undefined, "Count:" + String(match[0].count), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Dec */1); - }) - }, "-"), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Inc */0); - }) - }, "+")); + var initialCount = Props.initialCount + var match = React.useReducer(reducer, initialCount, init) + var dispatch = match[1] + return React.createElement( + React.Fragment, + undefined, + "Count:" + String(match[0].count), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Dec */ 1) + }, + }, + "-", + ), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Inc */ 0) + }, + }, + "+", + ), + ) } ``` diff --git a/pages/docs/react/v0.10.0/hooks-ref.mdx b/pages/docs/react/v0.10.0/hooks-ref.mdx index a3d596f7c..6af8d8b12 100644 --- a/pages/docs/react/v0.10.0/hooks-ref.mdx +++ b/pages/docs/react/v0.10.0/hooks-ref.mdx @@ -21,8 +21,8 @@ let refContainer = React.useRef(initialValue); ``` ```js - var button = React.useRef(null); - React.useRef(0); +var button = React.useRef(null) +React.useRef(0) ``` @@ -37,7 +37,6 @@ However, `useRef()` is useful for more than the ref attribute. It's handy for ke This works because `useRef()` creates a plain JavaScript object. The only difference between `useRef()` and creating a `{current: ...}` object yourself is that useRef will give you the same ref object on every render. - Keep in mind that `useRef` doesn’t notify you when its content changes. Mutating the `.current` record field doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a [callback ref](./refs-and-the-dom#callback-refs) instead. More infos on direct DOM manipulation can be found in the [Refs and the DOM](./refs-and-the-dom) section. @@ -46,7 +45,6 @@ More infos on direct DOM manipulation can be found in the [Refs and the DOM](./r ### Managing Focus for a Text Input - ```res @@ -73,19 +71,30 @@ let make = () => { ```js function TextInputWithFocusButton(Props) { - var inputEl = React.useRef(null); + var inputEl = React.useRef(null) var onClick = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(inputEl.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement(React.Fragment, undefined, React.createElement("input", { - ref: inputEl, - type: "text" - }), React.createElement("button", { - onClick: onClick - }, "Focus the input")); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(inputEl.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + React.Fragment, + undefined, + React.createElement("input", { + ref: inputEl, + type: "text", + }), + React.createElement( + "button", + { + onClick: onClick, + }, + "Focus the input", + ), + ) } ``` @@ -126,25 +135,31 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setTextInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } var focusTextInput = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: setTextInputRef, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: focusTextInput - })); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(textInput.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setTextInputRef, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: focusTextInput, + }), + ) } ``` diff --git a/pages/docs/react/v0.10.0/hooks-state.mdx b/pages/docs/react/v0.10.0/hooks-state.mdx index a3a9793c9..2d745c5f2 100644 --- a/pages/docs/react/v0.10.0/hooks-state.mdx +++ b/pages/docs/react/v0.10.0/hooks-state.mdx @@ -22,17 +22,16 @@ let (state, setState) = React.useState(_ => initialState) ```js var match = React.useState(function () { - return initialState; - }); + return initialState +}) -var state = match[0]; +var state = match[0] -var setState = match[1]; +var setState = match[1] ``` - During the initial render, the returned state `state` is the same as the value passed as the first argument (initialState). The `setState` function can be passed down to other components as well, which is useful for e.g. setting the state of a parent component by its children. @@ -62,7 +61,6 @@ let make = () => { In this example, we are creating a `ThemeContainer` component that manages a `darkmode` boolean state and passes the `setDarkmode` function to a `ControlPanel` component to trigger the state changes. - ```res @@ -95,37 +93,54 @@ let make = (~content) => { } ``` + ```js function ControlPanel(Props) { - var setDarkmode = Props.setDarkmode; - var darkmode = Props.darkmode; + var setDarkmode = Props.setDarkmode + var darkmode = Props.darkmode var onClick = function (evt) { - evt.preventDefault(); - return Curry._1(setDarkmode, (function (prev) { - return !prev; - })); - }; - var toggleText = "Switch to " + (( - darkmode ? "light" : "dark" - ) + " theme"); - return React.createElement("div", undefined, React.createElement("button", { - onClick: onClick - }, toggleText)); + evt.preventDefault() + return Curry._1(setDarkmode, function (prev) { + return !prev + }) + } + var toggleText = "Switch to " + ((darkmode ? "light" : "dark") + " theme") + return React.createElement( + "div", + undefined, + React.createElement( + "button", + { + onClick: onClick, + }, + toggleText, + ), + ) } function ThemeContainer(Props) { - var content = Props.content; + var content = Props.content var match = React.useState(function () { - return false; - }); - var darkmode = match[0]; - var className = darkmode ? "theme-dark" : "theme-light"; - return React.createElement("div", { - className: className - }, React.createElement("section", undefined, React.createElement("h1", undefined, "More Infos about ReScript"), content), React.createElement(Playground$ControlPanel, { - setDarkmode: match[1], - darkmode: darkmode - })); + return false + }) + var darkmode = match[0] + var className = darkmode ? "theme-dark" : "theme-light" + return React.createElement( + "div", + { + className: className, + }, + React.createElement( + "section", + undefined, + React.createElement("h1", undefined, "More Infos about ReScript"), + content, + ), + React.createElement(Playground$ControlPanel, { + setDarkmode: match[1], + darkmode: darkmode, + }), + ) } ``` @@ -133,7 +148,6 @@ function ThemeContainer(Props) { Note that whenever `setDarkmode` is returning a new value (e.g. switching from `true` -> `false`), it will cause a re-render for `ThemeContainer`'s `className` and the toggle text of its nested `ControlPanel`. - ## Uncurried Version For cleaner JS output, you can use `React.Uncurried.useState` instead: @@ -148,15 +162,14 @@ setState(. prev => prev + 1) ```js var match = React.useState(function () { - return 0; - }); + return 0 +}) -var setState = match[1]; +var setState = match[1] setState(function (prev) { - return prev + 1 | 0; - }); + return (prev + 1) | 0 +}) ``` - diff --git a/pages/docs/react/v0.10.0/refs-and-the-dom.mdx b/pages/docs/react/v0.10.0/refs-and-the-dom.mdx index db14f0e53..aaca7702f 100644 --- a/pages/docs/react/v0.10.0/refs-and-the-dom.mdx +++ b/pages/docs/react/v0.10.0/refs-and-the-dom.mdx @@ -12,7 +12,6 @@ Refs provide a way to access DOM nodes or React elements created within your `ma - In the typical React dataflow, [props](./components-and-props) are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an `React.element`, or it could be a `Dom.element`. For both of these cases, React provides an escape hatch. A `React.ref` is defined like this: @@ -21,13 +20,13 @@ A `React.ref` is defined like this: type t<'value> = { mutable current: 'value } ``` -> *Note that the `Ref.ref` should not to be confused with the builtin [ref type](/docs/manual/latest/mutation), the language feature that enables mutation.* +> _Note that the `Ref.ref` should not to be confused with the builtin [ref type](/docs/manual/latest/mutation), the language feature that enables mutation._ ## When to use Refs There are a few good use cases for refs: -- Managing state that *should not trigger* any re-render. +- Managing state that _should not trigger_ any re-render. - Managing focus, text selection, or media playback. - Triggering imperative animations. - Integrating with third-party DOM libraries. @@ -64,6 +63,7 @@ let value = myRef.current ``` The value of the ref differs depending on the type of the node: + - When the ref attribute is used on an HTML element, the ref passed via `ReactDOM.Ref.domRef` receives the underlying DOM element as its current property (type of `React.ref>`) - In case of interop, when the ref attribute is used on a custom class component (based on JS classes), the ref object receives the mounted instance of the component as its current (not discussed in this document). - **You may not use the ref attribute on component functions** because they don’t have instances (we don't expose JS classes in ReScript). @@ -102,23 +102,27 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var onClick = function (param) { - var dom = textInput.current; + var dom = textInput.current if (!(dom == null)) { - dom.focus(); - return ; + dom.focus() + return } - - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: textInput, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: onClick - })); + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: textInput, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: onClick, + }), + ) } ``` @@ -132,12 +136,10 @@ A few things happened here, so let's break them down: React will assign the `current` field with the DOM element when the component mounts, and assign it back to null when it unmounts. - ### Refs and Component Functions In React, you **can't** pass a `ref` attribute to a component function: - ```res @@ -167,7 +169,6 @@ The snippet above will not compile and output an error that looks like this: `"R As the error message implies, If you want to allow people to take a ref to your component function, you can use [ref forwarding](./forwarding-refs) (possibly in conjunction with useImperativeHandle) instead. - ## Exposing DOM Refs to Parent Components In rare cases, you might want to have access to a child’s DOM node from a parent component. This is generally not recommended because it breaks component encapsulation, but it can occasionally be useful for triggering focus or measuring the size or position of a child DOM node. @@ -215,25 +216,31 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setTextInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } var focusTextInput = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: setTextInputRef, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: focusTextInput - })); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(textInput.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setTextInputRef, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: focusTextInput, + }), + ) } ``` @@ -270,31 +277,32 @@ let make = () => { ```js function CustomTextInput(Props) { - var setInputRef = Props.setInputRef; - return React.createElement("div", undefined, React.createElement("input", { - ref: setInputRef, - type: "text" - })); + var setInputRef = Props.setInputRef + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setInputRef, + type: "text", + }), + ) } var CustomTextInput = { - make: CustomTextInput -}; + make: CustomTextInput, +} function Parent(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } return React.createElement(CustomTextInput, { - setInputRef: setInputRef - }); + setInputRef: setInputRef, + }) } ``` - In the example above, `Parent` passes its ref callback as an `setInputRef` prop to the `CustomTextInput`, and the `CustomTextInput` passes the same function as a special ref attribute to the ``. As a result, the `textInput` ref in Parent will be set to the DOM node corresponding to the `` element in the `CustomTextInput`. - diff --git a/pages/docs/react/v0.10.0/rendering-elements.mdx b/pages/docs/react/v0.10.0/rendering-elements.mdx index 55c3af853..f5b02bd54 100644 --- a/pages/docs/react/v0.10.0/rendering-elements.mdx +++ b/pages/docs/react/v0.10.0/rendering-elements.mdx @@ -25,7 +25,7 @@ Unlike browser DOM elements, React elements are plain objects, and are cheap to Let's assume we've got an HTML file that contains a `div` like this: ```html -
+
``` We call this a “root” DOM node because everything inside it will be managed by React DOM. @@ -33,6 +33,7 @@ We call this a “root” DOM node because everything inside it will be managed Plain React applications usually have a single root DOM node. If you are integrating React into an existing app, you may have as many isolated root DOM nodes as you like. To render our React application into the `root` div, we need to do two things: + - Find our DOM node with `ReactDOM.querySelector` - Render our React element to our queried `Dom.element` with `ReactDOM.render` @@ -48,11 +49,12 @@ switch(ReactDOM.querySelector("#root")){ | None => () // do nothing } ``` + ```js -var root = document.querySelector("#root"); +var root = document.querySelector("#root") if (!(root == null)) { - ReactDom.render(React.createElement("div", undefined, "Hello Andrea"), root); + ReactDom.render(React.createElement("div", undefined, "Hello Andrea"), root) } ``` diff --git a/pages/docs/react/v0.10.0/router.mdx b/pages/docs/react/v0.10.0/router.mdx index 0ceaaf0c0..626640e26 100644 --- a/pages/docs/react/v0.10.0/router.mdx +++ b/pages/docs/react/v0.10.0/router.mdx @@ -15,18 +15,19 @@ RescriptReact comes with a router! We've leveraged the language and library feat ## How does it work? The available methods are listed here: - - `RescriptReactRouter.push(string)`: takes a new path and update the URL. - - `RescriptReactRouter.replace(string)`: like `push`, but replaces the current URL. - - `RescriptReactRouter.watchUrl(f)`: start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the `RescriptReactRouter.url` record. - - `RescriptReactRouter.unwatchUrl(watcherID)`: stop watching for URL changes. - - `RescriptReactRouter.dangerouslyGetInitialUrl()`: get `url` record outside of `watchUrl`. Described later. - - `RescriptReactRouter.useUrl(~serverUrl)`: returns the `url` record inside a component. + +- `RescriptReactRouter.push(string)`: takes a new path and update the URL. +- `RescriptReactRouter.replace(string)`: like `push`, but replaces the current URL. +- `RescriptReactRouter.watchUrl(f)`: start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the `RescriptReactRouter.url` record. +- `RescriptReactRouter.unwatchUrl(watcherID)`: stop watching for URL changes. +- `RescriptReactRouter.dangerouslyGetInitialUrl()`: get `url` record outside of `watchUrl`. Described later. +- `RescriptReactRouter.useUrl(~serverUrl)`: returns the `url` record inside a component. > If you want to know more about the low level details on how the router interface is implemented, refer to the [RescriptReactRouter implementation](https://github.com/rescript-lang/rescript-react/blob/master/src/RescriptReactRouter.res). ## Match a Route -*There's no API*! `watchUrl` gives you back a `url` record of the following shape: +_There's no API_! `watchUrl` gives you back a `url` record of the following shape: @@ -40,6 +41,7 @@ type url = { search: string } ``` + ```js // Empty output ``` @@ -57,6 +59,7 @@ So the url `www.hello.com/book/10/edit?name=Jane#author` is given back as: search: "name=Jane" } ``` + ```js // Empty output ``` @@ -74,7 +77,7 @@ Let's start with a first example to see how a ReScript React Router looks like: @react.component let make = () => { let url = RescriptReactRouter.useUrl() - + switch url.path { | list{"user", id} => | list{} => @@ -82,37 +85,34 @@ let make = () => { } } ``` + ```js -import * as React from "react"; -import * as User from "./User.bs.js"; -import * as RescriptReactRouter from "@rescript/react/src/RescriptReactRouter.bs.js"; -import * as Home from "./Home.bs.js"; -import * as NotFound from "./NotFound.bs.js"; +import * as React from "react" +import * as User from "./User.bs.js" +import * as RescriptReactRouter from "@rescript/react/src/RescriptReactRouter.bs.js" +import * as Home from "./Home.bs.js" +import * as NotFound from "./NotFound.bs.js" function App(Props) { - var url = RescriptReactRouter.useUrl(undefined, undefined); - var match = url.path; + var url = RescriptReactRouter.useUrl(undefined, undefined) + var match = url.path if (!match) { - return React.createElement(Home.make, {}); + return React.createElement(Home.make, {}) } if (match.hd === "user") { - var match$1 = match.tl; + var match$1 = match.tl if (match$1 && !match$1.tl) { return React.createElement(User.make, { - id: match$1.hd - }); + id: match$1.hd, + }) } - } - return React.createElement(NotFound.make, {}); + return React.createElement(NotFound.make, {}) } -var make = App; +var make = App -export { - make , - -} +export { make } ``` @@ -126,6 +126,7 @@ In other words, you'd like to read from the `url` record once at the beginning o Note: the reason why we label it as "dangerous" is to remind you not to read this `url` in any arbitrary component's e.g. `render`, since that information might be out of date if said component doesn't also contain a `watchUrl` subscription that re-renders the component when the URL changes. Aka, please only use `dangerouslyGetInitialUrl` alongside `watchUrl`. ## Push a New Route + From anywhere in your app, just call e.g. `RescriptReactRouter.push("/books/10/edit#validated")`. This will trigger a URL change (without a page refresh) and `watchUrl`'s callback will be called again. We might provide better facilities for typed routing + payload carrying in the future! diff --git a/pages/docs/react/v0.11.0/arrays-and-keys.mdx b/pages/docs/react/v0.11.0/arrays-and-keys.mdx index 56be3f290..7543924ac 100644 --- a/pages/docs/react/v0.11.0/arrays-and-keys.mdx +++ b/pages/docs/react/v0.11.0/arrays-and-keys.mdx @@ -77,7 +77,7 @@ module Blog = {

{React.string(post.content)}

}); - +
{sidebar}
@@ -94,7 +94,6 @@ let posts = [ let blog = ``` - ## Rendering `list` Values In case you ever want to render a `list` of items, you can do something like this: @@ -124,4 +123,3 @@ let make = () => { We use `Belt.List.toArray` to convert our list to an array before creating our `array`. Please note that using `list` has performance impact due to extra conversion costs. 99% of the time you'll want to use arrays (seamless interop, faster JS code), but in some cases it might make sense to use a `list` to leverage advanced pattern matching features etc. - diff --git a/pages/docs/react/v0.11.0/beyond-jsx.mdx b/pages/docs/react/v0.11.0/beyond-jsx.mdx index bb4a12411..629b309d4 100644 --- a/pages/docs/react/v0.11.0/beyond-jsx.mdx +++ b/pages/docs/react/v0.11.0/beyond-jsx.mdx @@ -14,11 +14,11 @@ JSX is a syntax sugar that allows us to use React components in an HTML like man **Note:** This section requires knowledge about the low level apis for [creating elements](./elements-and-jsx#creating-elements-from-component-functions), such as `React.createElement` or `ReactDOM.createDOMElementVariadic`. -> **Note:** This page assumes your `bsconfig.json` to be set to `"jsx": { "version": 4 }` to apply the right JSX transformations. +> **Note:** This page assumes your `bsconfig.json` to be set to `"jsx": { "version": 4 }` to apply the right JSX transformations. ## Component Types -A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component<'props>`. +A plain React component is defined as a `('props) => React.element` function. You can also express a component more efficiently with our shorthand type `React.component<'props>`. Here are some examples on how to define your own component types (often useful when interoping with existing JS code, or passing around components): @@ -32,6 +32,7 @@ type friendComp = friend => React.element type props = {padding: string, children: React.element} type containerComp = React.component ``` + The types above are pretty low level (basically the JS representation of a React component), but since ReScript React has its own ways of defining React components in a more language specific way, let's have a closer look on the anatomy of such a construct. ## JSX Component Interface @@ -51,6 +52,7 @@ module Friend = { } } ``` + ```res module Friend = { type props<'name, 'children> = { @@ -75,7 +77,6 @@ In the expanded output: The `@react.component` decorator also works for `React.forwardRef` calls: - ```res @@ -112,13 +113,14 @@ So now that we know how the ReScript React component transformation works, let's ## JSX Under the Hood -Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children: +Whenever we are using JSX with a custom component ("capitalized JSX"), we are actually using `React.createElement` to create a new element. Here is an example of a React component without children: ```res ``` + ```res // classic React.createElement(Friend.make, {name: "Fred", age:20}) @@ -126,8 +128,9 @@ React.createElement(Friend.make, {name: "Fred", age:20}) // automatic React.jsx(Friend.make, {name: "Fred", age: 20}) ``` + ```js -React.createElement(Playground$Friend, { name: "Fred", age: 20 }); +React.createElement(Playground$Friend, { name: "Fred", age: 20 }) ``` @@ -159,17 +162,16 @@ React.jsxs( ``` ```js -React.createElement(Container, { width: 200, children: null }, "Hello", "World"); +React.createElement(Container, { width: 200, children: null }, "Hello", "World") ``` Note that the `children: React.null` field has no relevance since React will only care about the children array passed as a third argument. - ### Dom Elements -"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOM.createDOMElementVariadic` calls: +"Uncapitalized JSX" expressions are treated as DOM elements and will be converted to `ReactDOM.createDOMElementVariadic` calls: @@ -186,7 +188,7 @@ ReactDOM.jsx("div", {title: "test"}) ``` ```js -React.createElement("div", { title: "test" }); +React.createElement("div", { title: "test" }) ``` @@ -214,7 +216,11 @@ ReactDOM.jsx("div", {title: "test", children: ?ReactDOM.someElement(ReactDOM.jsx ``` ```js -React.createElement("div", { title: "test" }, React.createElement("span", undefined)); +React.createElement( + "div", + { title: "test" }, + React.createElement("span", undefined), +) ``` diff --git a/pages/docs/react/v0.11.0/components-and-props.mdx b/pages/docs/react/v0.11.0/components-and-props.mdx index 7d21b1d68..e6903e23f 100644 --- a/pages/docs/react/v0.11.0/components-and-props.mdx +++ b/pages/docs/react/v0.11.0/components-and-props.mdx @@ -8,13 +8,13 @@ canonical: "/docs/react/latest/components-and-props" -Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. +Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components. ## What is a Component? -A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`. +A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`. The nice thing about this concept is that you can solely focus on the input and output. The component function receives some data and returns some opaque `React.element` that is managed by the React framework to render your UI. @@ -35,14 +35,15 @@ let make = () => {
} ``` + ```js -import * as React from "react"; +import * as React from "react" function Greeting(props) { - return React.createElement("div", undefined, "Hello ReScripters!"); + return React.createElement("div", undefined, "Hello ReScripters!") } -var make = Greeting; +var make = Greeting ``` @@ -51,7 +52,7 @@ var make = Greeting; We've created a `Greeting.res` file that contains a `make` function that doesn't receive any props (the function doesn't receive any parameters), and returns a `React.element` that represents `
Hello ReScripters!
` in the rendered DOM. -You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `React.createElement("div",...)` call in JavaScript. +You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `
` transforms into a `React.createElement("div",...)` call in JavaScript. ## Defining Props @@ -71,15 +72,22 @@ let make = (~title: string, ~visitorCount: int, ~children: React.element) => {
} ``` + ```js -import * as React from "react"; +import * as React from "react" function Article(props) { - var visitorCountMsg = "You are visitor number: " + String(props.visitorCount); - return React.createElement("div", undefined, React.createElement("div", undefined, props.title), React.createElement("div", undefined, visitorCountMsg), props.children); + var visitorCountMsg = "You are visitor number: " + String(props.visitorCount) + return React.createElement( + "div", + undefined, + React.createElement("div", undefined, props.title), + React.createElement("div", undefined, visitorCountMsg), + props.children, + ) } -var make = Article; +var make = Article ``` @@ -104,15 +112,15 @@ let make = (~name: option=?) => { ```js function Greeting(props) { - var name = props.name; - var greeting = name !== undefined ? "Hello " + name + "!" : "Hello stranger!"; - return React.createElement("div", undefined, greeting); + var name = props.name + var greeting = name !== undefined ? "Hello " + name + "!" : "Hello stranger!" + return React.createElement("div", undefined, greeting) } ``` -**Note:** The `@react.component` attribute implicitly adds the last `()` parameter to our `make` function for us (no need to do it ourselves). +**Note:** The `@react.component` attribute implicitly adds the last `()` parameter to our `make` function for us (no need to do it ourselves). In JSX, you can apply optional props with some special syntax: @@ -125,11 +133,11 @@ let name = Some("Andrea") ``` ```js -var name = "Andrea"; +var name = "Andrea" React.createElement(Greeting, { - name: name -}); + name: name, +}) ``` @@ -142,13 +150,12 @@ Check out the corresponding [Arrays and Keys](./arrays-and-keys) and [Forwarding ### Handling Invalid Prop Names (e.g. keywords) -Prop names like `type` (as in ``) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `` instead. +Prop names like `type` (as in ``) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `` instead. For `aria-*` use camelCasing, e.g., `ariaLabel`. For DOM components, we'll translate it to `aria-label` under the hood. For `data-*` this is a bit trickier; words with `-` in them aren't valid in ReScript. When you do want to write them, e.g., `
`, check out the [React.cloneElement](./elements-and-jsx#cloning-elements) or [React.createDOMElementVariadic](./elements-and-jsx#creating-dom-elements) section. - ## Children Props In React `props.children` is a special attribute to represent the nested elements within a parent element: @@ -180,17 +187,21 @@ module MyList = { ```js function MyList(props) { - return React.createElement("ul", undefined, props.children); + return React.createElement("ul", undefined, props.children) } var MyList = { - make: MyList -}; + make: MyList, +} -React.createElement(MyList, { - children: null - }, React.createElement("li", undefined, "Item 1"), - React.createElement("li", undefined, "Item 2")); +React.createElement( + MyList, + { + children: null, + }, + React.createElement("li", undefined, "Item 1"), + React.createElement("li", undefined, "Item 2"), +) ``` @@ -260,14 +271,12 @@ The best way to approach this kind of issue is by using props instead of childre **The best use-case for `children` is to pass down `React.element`s without any semantic order or implementation details!** - ## Props & Type Inference -The ReScript type system is really good at inferring the prop types just by looking at its prop usage. +The ReScript type system is really good at inferring the prop types just by looking at its prop usage. For simple cases, well-scoped usage, or experimentation, it's still fine to omit type annotations: - ```res // Button.res @@ -300,22 +309,26 @@ let make = () => {
} ``` + ```js -var React = require("react"); +var React = require("react") var Greeting = require("./Greeting.js") function App(Props) { - return React.createElement("div", undefined, React.createElement(Greeting.make, {})); + return React.createElement( + "div", + undefined, + React.createElement(Greeting.make, {}), + ) } -var make = App; +var make = App ``` **Note:** React components are capitalized; primitive DOM elements like `div` or `button` are uncapitalized. More infos on the JSX specifics and code transformations can be found in our [JSX language manual section](/docs/manual/latest/jsx#capitalized-tag). - ### Handwritten Components You don't need to use the `@react.component` decorator to write components that can be used in JSX. Instead you can write the `make` function with type `props` and these will always work as React components. But then you will have the issue with the component name being "make" in the React dev tools. @@ -327,8 +340,8 @@ For example: ```res module Link = { type props = {href: string, children: React.element}; - - let make = (props: props) => { + + let make = (props: props) => { {props.children} @@ -337,33 +350,29 @@ module Link = { {React.string("Docs")} ``` + ```js function make(props) { - return React.createElement( - "a", - { href: props.href }, - props.children - ); + return React.createElement("a", { href: props.href }, props.children) } var Link = { make: make, -}; +} React.createElement(make, { href: "/docs", children: "Docs", -}); +}) ``` More details on the `@react.component` decorator and its generated interface can be found in our [Beyond JSX](./beyond-jsx) page. - ## Submodule Components -We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): +We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): ```res // src/Button.res @@ -385,7 +394,6 @@ let make = (~children) => { The `Button.res` file defined above is now containing a `Label` component, that can also be used by other components, either by writing the fully qualified module name (``) or by using a module alias to shortcut the full qualifier: - ```res module Label = Button.Label @@ -396,7 +404,6 @@ let content =
} ``` + ```js function Counter(Props) { var match = React.useState(function () { - return 0; - }); - var setCount = match[1]; + return 0 + }) + var setCount = match[1] var onClick = function (_evt) { - return Curry._1(setCount, (function (prev) { - return prev + 1 | 0; - })); - }; - var msg = "You clicked" + String(match[0]) + "times"; - return React.createElement("div", undefined, React.createElement("p", undefined, msg), React.createElement("button", { - onClick: onClick - }, "Click me")); + return Curry._1(setCount, function (prev) { + return (prev + 1) | 0 + }) + } + var msg = "You clicked" + String(match[0]) + "times" + return React.createElement( + "div", + undefined, + React.createElement("p", undefined, msg), + React.createElement( + "button", + { + onClick: onClick, + }, + "Click me", + ), + ) } ``` - -Here we are using the `React.useState` Hook. We call it inside a component function to add some local state to it. React will preserve this state between re-renders. `React.useState` returns a tuple: the current state value (`count`) and a function that lets you update it (`setCount`). You can call this function from an event handler or pass it down to other components to call the function. +Here we are using the `React.useState` Hook. We call it inside a component function to add some local state to it. React will preserve this state between re-renders. `React.useState` returns a tuple: the current state value (`count`) and a function that lets you update it (`setCount`). You can call this function from an event handler or pass it down to other components to call the function. The only argument to `React.useState` is a function that returns the initial state (`_ => 0`). In the example above, it is 0 because our counter starts from zero. Note that your state can be any type you want and `ReScript` will make sure to infer the types for you (only make sure to return an initial state that matches your type). The initial state argument is only used during the first render. @@ -84,12 +93,11 @@ This was just a quick example on our first hook usage. We will go into more deta ### Additional Hooks: - [useReducer](./hooks-reducer): An alternative to `useState`. Uses the state / action / reduce pattern. - - + + - [useRef](./hooks-ref): Returns a mutable React-Ref value - - - + + ## Rules of Hooks @@ -97,10 +105,8 @@ Hooks are just simple functions, but you need to follow _two rules_ when using t ### Rule 1) Only Call Hooks at the Top Level - **Don’t call Hooks inside loops, conditions, or nested functions.** Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple `useState` and `useEffect` calls. (If you’re curious, you can check out the in depth explanation in the [ReactJS Hooks docs](https://reactjs.org/docs/hooks-rules.html#explanation)) - ### Rule 2) Only Call Hooks from React Functions **Don't call Hooks from regular functions.** Instead, you can: diff --git a/pages/docs/react/v0.11.0/hooks-reducer.mdx b/pages/docs/react/v0.11.0/hooks-reducer.mdx index 885ac59ce..ed4e61583 100644 --- a/pages/docs/react/v0.11.0/hooks-reducer.mdx +++ b/pages/docs/react/v0.11.0/hooks-reducer.mdx @@ -21,12 +21,12 @@ let (state, dispatch) = React.useReducer(reducer, initialState) ``` ```js -var match = React.useReducer(reducer, initialState); +var match = React.useReducer(reducer, initialState) ``` -An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, action) => newState`, and returns the current `state` paired with a `dispatch` function `(action) => unit`. +An alternative to [useState](./hooks-state). Accepts a reducer of type `(state, action) => newState`, and returns the current `state` paired with a `dispatch` function `(action) => unit`. `React.useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks. @@ -67,29 +67,43 @@ let make = () => { function reducer(state, action) { if (action) { return { - count: state.count - 1 | 0 - }; + count: (state.count - 1) | 0, + } } else { return { - count: state.count + 1 | 0 - }; + count: (state.count + 1) | 0, + } } } function Counter(Props) { var match = React.useReducer(reducer, { - count: 0 - }); - var dispatch = match[1]; - return React.createElement(React.Fragment, undefined, "Count:" + String(match[0].count), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Dec */1); - }) - }, "-"), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Inc */0); - }) - }, "+")); + count: 0, + }) + var dispatch = match[1] + return React.createElement( + React.Fragment, + undefined, + "Count:" + String(match[0].count), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Dec */ 1) + }, + }, + "-", + ), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Inc */ 0) + }, + }, + "+", + ), + ) } ``` @@ -177,88 +191,103 @@ let make = () => { ```js function reducer(state, action) { switch (action.TAG | 0) { - case /* AddTodo */0 : - var todos = state.todos.concat([{ - id: state.nextId, - content: action._0, - completed: false - }]); - return { - todos: todos, - nextId: state.nextId + 1 | 0 - }; - case /* RemoveTodo */1 : - var id = action._0; - var todos$1 = state.todos.filter(function (todo) { - return todo.id !== id; - }); - return { - todos: todos$1, - nextId: state.nextId - }; - case /* ToggleTodo */2 : - var id$1 = action._0; - var todos$2 = Belt_Array.map(state.todos, (function (todo) { - if (todo.id === id$1) { - return { - id: todo.id, - content: todo.content, - completed: !todo.completed - }; - } else { - return todo; - } - })); - return { - todos: todos$2, - nextId: state.nextId - }; - + case /* AddTodo */ 0: + var todos = state.todos.concat([ + { + id: state.nextId, + content: action._0, + completed: false, + }, + ]) + return { + todos: todos, + nextId: (state.nextId + 1) | 0, + } + case /* RemoveTodo */ 1: + var id = action._0 + var todos$1 = state.todos.filter(function (todo) { + return todo.id !== id + }) + return { + todos: todos$1, + nextId: state.nextId, + } + case /* ToggleTodo */ 2: + var id$1 = action._0 + var todos$2 = Belt_Array.map(state.todos, function (todo) { + if (todo.id === id$1) { + return { + id: todo.id, + content: todo.content, + completed: !todo.completed, + } + } else { + return todo + } + }) + return { + todos: todos$2, + nextId: state.nextId, + } } } -var initialTodos = [{ +var initialTodos = [ + { id: 1, content: "Try ReScript & React", - completed: false - }]; + completed: false, + }, +] function TodoApp(Props) { var match = React.useReducer(reducer, { - todos: initialTodos, - nextId: 2 - }); - var dispatch = match[1]; - var todos = Belt_Array.map(match[0].todos, (function (todo) { - return React.createElement("li", undefined, todo.content, React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, { - TAG: /* RemoveTodo */1, - _0: todo.id - }); - }) - }, "Remove"), React.createElement("input", { - checked: todo.completed, - type: "checkbox", - onChange: (function (param) { - return Curry._1(dispatch, { - TAG: /* ToggleTodo */2, - _0: todo.id - }); - }) - })); - })); - return React.createElement(React.Fragment, undefined, React.createElement("h1", undefined, "Todo List:"), React.createElement("ul", undefined, todos)); + todos: initialTodos, + nextId: 2, + }) + var dispatch = match[1] + var todos = Belt_Array.map(match[0].todos, function (todo) { + return React.createElement( + "li", + undefined, + todo.content, + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, { + TAG: /* RemoveTodo */ 1, + _0: todo.id, + }) + }, + }, + "Remove", + ), + React.createElement("input", { + checked: todo.completed, + type: "checkbox", + onChange: function (param) { + return Curry._1(dispatch, { + TAG: /* ToggleTodo */ 2, + _0: todo.id, + }) + }, + }), + ) + }) + return React.createElement( + React.Fragment, + undefined, + React.createElement("h1", undefined, "Todo List:"), + React.createElement("ul", undefined, todos), + ) } ``` - ## Lazy Initialization - - ```res @@ -267,7 +296,7 @@ let (state, dispatch) = ``` ```js -var match = React.useReducer(reducer, initialState, init); +var match = React.useReducer(reducer, initialState, init) ``` @@ -315,41 +344,55 @@ let make = (~initialCount: int) => { ```js function init(initialCount) { return { - count: initialCount - }; + count: initialCount, + } } function reducer(state, action) { if (typeof action === "number") { if (action !== 0) { return { - count: state.count - 1 | 0 - }; + count: (state.count - 1) | 0, + } } else { return { - count: state.count + 1 | 0 - }; + count: (state.count + 1) | 0, + } } } else { return { - count: action._0 - }; + count: action._0, + } } } function Counter(Props) { - var initialCount = Props.initialCount; - var match = React.useReducer(reducer, initialCount, init); - var dispatch = match[1]; - return React.createElement(React.Fragment, undefined, "Count:" + String(match[0].count), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Dec */1); - }) - }, "-"), React.createElement("button", { - onClick: (function (param) { - return Curry._1(dispatch, /* Inc */0); - }) - }, "+")); + var initialCount = Props.initialCount + var match = React.useReducer(reducer, initialCount, init) + var dispatch = match[1] + return React.createElement( + React.Fragment, + undefined, + "Count:" + String(match[0].count), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Dec */ 1) + }, + }, + "-", + ), + React.createElement( + "button", + { + onClick: function (param) { + return Curry._1(dispatch, /* Inc */ 0) + }, + }, + "+", + ), + ) } ``` diff --git a/pages/docs/react/v0.11.0/hooks-ref.mdx b/pages/docs/react/v0.11.0/hooks-ref.mdx index a3d596f7c..6af8d8b12 100644 --- a/pages/docs/react/v0.11.0/hooks-ref.mdx +++ b/pages/docs/react/v0.11.0/hooks-ref.mdx @@ -21,8 +21,8 @@ let refContainer = React.useRef(initialValue); ``` ```js - var button = React.useRef(null); - React.useRef(0); +var button = React.useRef(null) +React.useRef(0) ``` @@ -37,7 +37,6 @@ However, `useRef()` is useful for more than the ref attribute. It's handy for ke This works because `useRef()` creates a plain JavaScript object. The only difference between `useRef()` and creating a `{current: ...}` object yourself is that useRef will give you the same ref object on every render. - Keep in mind that `useRef` doesn’t notify you when its content changes. Mutating the `.current` record field doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a [callback ref](./refs-and-the-dom#callback-refs) instead. More infos on direct DOM manipulation can be found in the [Refs and the DOM](./refs-and-the-dom) section. @@ -46,7 +45,6 @@ More infos on direct DOM manipulation can be found in the [Refs and the DOM](./r ### Managing Focus for a Text Input - ```res @@ -73,19 +71,30 @@ let make = () => { ```js function TextInputWithFocusButton(Props) { - var inputEl = React.useRef(null); + var inputEl = React.useRef(null) var onClick = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(inputEl.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement(React.Fragment, undefined, React.createElement("input", { - ref: inputEl, - type: "text" - }), React.createElement("button", { - onClick: onClick - }, "Focus the input")); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(inputEl.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + React.Fragment, + undefined, + React.createElement("input", { + ref: inputEl, + type: "text", + }), + React.createElement( + "button", + { + onClick: onClick, + }, + "Focus the input", + ), + ) } ``` @@ -126,25 +135,31 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setTextInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } var focusTextInput = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: setTextInputRef, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: focusTextInput - })); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(textInput.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setTextInputRef, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: focusTextInput, + }), + ) } ``` diff --git a/pages/docs/react/v0.11.0/hooks-state.mdx b/pages/docs/react/v0.11.0/hooks-state.mdx index a3a9793c9..2d745c5f2 100644 --- a/pages/docs/react/v0.11.0/hooks-state.mdx +++ b/pages/docs/react/v0.11.0/hooks-state.mdx @@ -22,17 +22,16 @@ let (state, setState) = React.useState(_ => initialState) ```js var match = React.useState(function () { - return initialState; - }); + return initialState +}) -var state = match[0]; +var state = match[0] -var setState = match[1]; +var setState = match[1] ``` - During the initial render, the returned state `state` is the same as the value passed as the first argument (initialState). The `setState` function can be passed down to other components as well, which is useful for e.g. setting the state of a parent component by its children. @@ -62,7 +61,6 @@ let make = () => { In this example, we are creating a `ThemeContainer` component that manages a `darkmode` boolean state and passes the `setDarkmode` function to a `ControlPanel` component to trigger the state changes. - ```res @@ -95,37 +93,54 @@ let make = (~content) => {
} ``` + ```js function ControlPanel(Props) { - var setDarkmode = Props.setDarkmode; - var darkmode = Props.darkmode; + var setDarkmode = Props.setDarkmode + var darkmode = Props.darkmode var onClick = function (evt) { - evt.preventDefault(); - return Curry._1(setDarkmode, (function (prev) { - return !prev; - })); - }; - var toggleText = "Switch to " + (( - darkmode ? "light" : "dark" - ) + " theme"); - return React.createElement("div", undefined, React.createElement("button", { - onClick: onClick - }, toggleText)); + evt.preventDefault() + return Curry._1(setDarkmode, function (prev) { + return !prev + }) + } + var toggleText = "Switch to " + ((darkmode ? "light" : "dark") + " theme") + return React.createElement( + "div", + undefined, + React.createElement( + "button", + { + onClick: onClick, + }, + toggleText, + ), + ) } function ThemeContainer(Props) { - var content = Props.content; + var content = Props.content var match = React.useState(function () { - return false; - }); - var darkmode = match[0]; - var className = darkmode ? "theme-dark" : "theme-light"; - return React.createElement("div", { - className: className - }, React.createElement("section", undefined, React.createElement("h1", undefined, "More Infos about ReScript"), content), React.createElement(Playground$ControlPanel, { - setDarkmode: match[1], - darkmode: darkmode - })); + return false + }) + var darkmode = match[0] + var className = darkmode ? "theme-dark" : "theme-light" + return React.createElement( + "div", + { + className: className, + }, + React.createElement( + "section", + undefined, + React.createElement("h1", undefined, "More Infos about ReScript"), + content, + ), + React.createElement(Playground$ControlPanel, { + setDarkmode: match[1], + darkmode: darkmode, + }), + ) } ``` @@ -133,7 +148,6 @@ function ThemeContainer(Props) { Note that whenever `setDarkmode` is returning a new value (e.g. switching from `true` -> `false`), it will cause a re-render for `ThemeContainer`'s `className` and the toggle text of its nested `ControlPanel`. - ## Uncurried Version For cleaner JS output, you can use `React.Uncurried.useState` instead: @@ -148,15 +162,14 @@ setState(. prev => prev + 1) ```js var match = React.useState(function () { - return 0; - }); + return 0 +}) -var setState = match[1]; +var setState = match[1] setState(function (prev) { - return prev + 1 | 0; - }); + return (prev + 1) | 0 +}) ``` - diff --git a/pages/docs/react/v0.11.0/refs-and-the-dom.mdx b/pages/docs/react/v0.11.0/refs-and-the-dom.mdx index db14f0e53..aaca7702f 100644 --- a/pages/docs/react/v0.11.0/refs-and-the-dom.mdx +++ b/pages/docs/react/v0.11.0/refs-and-the-dom.mdx @@ -12,7 +12,6 @@ Refs provide a way to access DOM nodes or React elements created within your `ma - In the typical React dataflow, [props](./components-and-props) are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an `React.element`, or it could be a `Dom.element`. For both of these cases, React provides an escape hatch. A `React.ref` is defined like this: @@ -21,13 +20,13 @@ A `React.ref` is defined like this: type t<'value> = { mutable current: 'value } ``` -> *Note that the `Ref.ref` should not to be confused with the builtin [ref type](/docs/manual/latest/mutation), the language feature that enables mutation.* +> _Note that the `Ref.ref` should not to be confused with the builtin [ref type](/docs/manual/latest/mutation), the language feature that enables mutation._ ## When to use Refs There are a few good use cases for refs: -- Managing state that *should not trigger* any re-render. +- Managing state that _should not trigger_ any re-render. - Managing focus, text selection, or media playback. - Triggering imperative animations. - Integrating with third-party DOM libraries. @@ -64,6 +63,7 @@ let value = myRef.current ``` The value of the ref differs depending on the type of the node: + - When the ref attribute is used on an HTML element, the ref passed via `ReactDOM.Ref.domRef` receives the underlying DOM element as its current property (type of `React.ref>`) - In case of interop, when the ref attribute is used on a custom class component (based on JS classes), the ref object receives the mounted instance of the component as its current (not discussed in this document). - **You may not use the ref attribute on component functions** because they don’t have instances (we don't expose JS classes in ReScript). @@ -102,23 +102,27 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var onClick = function (param) { - var dom = textInput.current; + var dom = textInput.current if (!(dom == null)) { - dom.focus(); - return ; + dom.focus() + return } - - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: textInput, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: onClick - })); + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: textInput, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: onClick, + }), + ) } ``` @@ -132,12 +136,10 @@ A few things happened here, so let's break them down: React will assign the `current` field with the DOM element when the component mounts, and assign it back to null when it unmounts. - ### Refs and Component Functions In React, you **can't** pass a `ref` attribute to a component function: - ```res @@ -167,7 +169,6 @@ The snippet above will not compile and output an error that looks like this: `"R As the error message implies, If you want to allow people to take a ref to your component function, you can use [ref forwarding](./forwarding-refs) (possibly in conjunction with useImperativeHandle) instead. - ## Exposing DOM Refs to Parent Components In rare cases, you might want to have access to a child’s DOM node from a parent component. This is generally not recommended because it breaks component encapsulation, but it can occasionally be useful for triggering focus or measuring the size or position of a child DOM node. @@ -215,25 +216,31 @@ let make = () => { ```js function CustomTextInput(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setTextInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } var focusTextInput = function (param) { - return Belt_Option.forEach(Caml_option.nullable_to_opt(textInput.current), (function (input) { - input.focus(); - - })); - }; - return React.createElement("div", undefined, React.createElement("input", { - ref: setTextInputRef, - type: "text" - }), React.createElement("input", { - type: "button", - value: "Focus the text input", - onClick: focusTextInput - })); + return Belt_Option.forEach( + Caml_option.nullable_to_opt(textInput.current), + function (input) { + input.focus() + }, + ) + } + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setTextInputRef, + type: "text", + }), + React.createElement("input", { + type: "button", + value: "Focus the text input", + onClick: focusTextInput, + }), + ) } ``` @@ -270,31 +277,32 @@ let make = () => { ```js function CustomTextInput(Props) { - var setInputRef = Props.setInputRef; - return React.createElement("div", undefined, React.createElement("input", { - ref: setInputRef, - type: "text" - })); + var setInputRef = Props.setInputRef + return React.createElement( + "div", + undefined, + React.createElement("input", { + ref: setInputRef, + type: "text", + }), + ) } var CustomTextInput = { - make: CustomTextInput -}; + make: CustomTextInput, +} function Parent(Props) { - var textInput = React.useRef(null); + var textInput = React.useRef(null) var setInputRef = function (element) { - textInput.current = element; - - }; + textInput.current = element + } return React.createElement(CustomTextInput, { - setInputRef: setInputRef - }); + setInputRef: setInputRef, + }) } ``` - In the example above, `Parent` passes its ref callback as an `setInputRef` prop to the `CustomTextInput`, and the `CustomTextInput` passes the same function as a special ref attribute to the ``. As a result, the `textInput` ref in Parent will be set to the DOM node corresponding to the `` element in the `CustomTextInput`. - diff --git a/pages/docs/react/v0.11.0/rendering-elements.mdx b/pages/docs/react/v0.11.0/rendering-elements.mdx index 06413ad0a..1894ea4e5 100644 --- a/pages/docs/react/v0.11.0/rendering-elements.mdx +++ b/pages/docs/react/v0.11.0/rendering-elements.mdx @@ -25,7 +25,7 @@ Unlike browser DOM elements, React elements are plain objects, and are cheap to Let's assume we've got an HTML file that contains a `div` like this: ```html -
+
``` We call this a “root” DOM node because everything inside it will be managed by React DOM. @@ -33,6 +33,7 @@ We call this a “root” DOM node because everything inside it will be managed Plain React applications usually have a single root DOM node. If you are integrating React into an existing app, you may have as many isolated root DOM nodes as you like. To render our React application into the `root` div, we need to do two things: + - Find our DOM node with `ReactDOM.querySelector` - Render our React element to our queried `Dom.element` with `ReactDOM.render` @@ -51,11 +52,12 @@ switch ReactDOM.querySelector("#root") { | None => () } ``` + ```js -var root = document.querySelector("#root"); +var root = document.querySelector("#root") if (!(root == null)) { - ReactDom.render(React.createElement("div", undefined, "Hello Andrea"), root); + ReactDom.render(React.createElement("div", undefined, "Hello Andrea"), root) } ``` diff --git a/pages/docs/react/v0.11.0/router.mdx b/pages/docs/react/v0.11.0/router.mdx index 0ceaaf0c0..626640e26 100644 --- a/pages/docs/react/v0.11.0/router.mdx +++ b/pages/docs/react/v0.11.0/router.mdx @@ -15,18 +15,19 @@ RescriptReact comes with a router! We've leveraged the language and library feat ## How does it work? The available methods are listed here: - - `RescriptReactRouter.push(string)`: takes a new path and update the URL. - - `RescriptReactRouter.replace(string)`: like `push`, but replaces the current URL. - - `RescriptReactRouter.watchUrl(f)`: start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the `RescriptReactRouter.url` record. - - `RescriptReactRouter.unwatchUrl(watcherID)`: stop watching for URL changes. - - `RescriptReactRouter.dangerouslyGetInitialUrl()`: get `url` record outside of `watchUrl`. Described later. - - `RescriptReactRouter.useUrl(~serverUrl)`: returns the `url` record inside a component. + +- `RescriptReactRouter.push(string)`: takes a new path and update the URL. +- `RescriptReactRouter.replace(string)`: like `push`, but replaces the current URL. +- `RescriptReactRouter.watchUrl(f)`: start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the `RescriptReactRouter.url` record. +- `RescriptReactRouter.unwatchUrl(watcherID)`: stop watching for URL changes. +- `RescriptReactRouter.dangerouslyGetInitialUrl()`: get `url` record outside of `watchUrl`. Described later. +- `RescriptReactRouter.useUrl(~serverUrl)`: returns the `url` record inside a component. > If you want to know more about the low level details on how the router interface is implemented, refer to the [RescriptReactRouter implementation](https://github.com/rescript-lang/rescript-react/blob/master/src/RescriptReactRouter.res). ## Match a Route -*There's no API*! `watchUrl` gives you back a `url` record of the following shape: +_There's no API_! `watchUrl` gives you back a `url` record of the following shape: @@ -40,6 +41,7 @@ type url = { search: string } ``` + ```js // Empty output ``` @@ -57,6 +59,7 @@ So the url `www.hello.com/book/10/edit?name=Jane#author` is given back as: search: "name=Jane" } ``` + ```js // Empty output ``` @@ -74,7 +77,7 @@ Let's start with a first example to see how a ReScript React Router looks like: @react.component let make = () => { let url = RescriptReactRouter.useUrl() - + switch url.path { | list{"user", id} => | list{} => @@ -82,37 +85,34 @@ let make = () => { } } ``` + ```js -import * as React from "react"; -import * as User from "./User.bs.js"; -import * as RescriptReactRouter from "@rescript/react/src/RescriptReactRouter.bs.js"; -import * as Home from "./Home.bs.js"; -import * as NotFound from "./NotFound.bs.js"; +import * as React from "react" +import * as User from "./User.bs.js" +import * as RescriptReactRouter from "@rescript/react/src/RescriptReactRouter.bs.js" +import * as Home from "./Home.bs.js" +import * as NotFound from "./NotFound.bs.js" function App(Props) { - var url = RescriptReactRouter.useUrl(undefined, undefined); - var match = url.path; + var url = RescriptReactRouter.useUrl(undefined, undefined) + var match = url.path if (!match) { - return React.createElement(Home.make, {}); + return React.createElement(Home.make, {}) } if (match.hd === "user") { - var match$1 = match.tl; + var match$1 = match.tl if (match$1 && !match$1.tl) { return React.createElement(User.make, { - id: match$1.hd - }); + id: match$1.hd, + }) } - } - return React.createElement(NotFound.make, {}); + return React.createElement(NotFound.make, {}) } -var make = App; +var make = App -export { - make , - -} +export { make } ``` @@ -126,6 +126,7 @@ In other words, you'd like to read from the `url` record once at the beginning o Note: the reason why we label it as "dangerous" is to remind you not to read this `url` in any arbitrary component's e.g. `render`, since that information might be out of date if said component doesn't also contain a `watchUrl` subscription that re-renders the component when the URL changes. Aka, please only use `dangerouslyGetInitialUrl` alongside `watchUrl`. ## Push a New Route + From anywhere in your app, just call e.g. `RescriptReactRouter.push("/books/10/edit#validated")`. This will trigger a URL change (without a page refresh) and `watchUrl`'s callback will be called again. We might provide better facilities for typed routing + payload carrying in the future! diff --git a/pages/docs/v10.0.0.js b/pages/docs/v10.0.0.js index 8762e51e7..9941d89f4 100644 --- a/pages/docs/v10.0.0.js +++ b/pages/docs/v10.0.0.js @@ -1,3 +1,3 @@ -import make from "src/DocsOverview.mjs"; +import make from "src/DocsOverview.mjs" -export default make; +export default make diff --git a/pages/docs/v11.0.0.js b/pages/docs/v11.0.0.js index 8762e51e7..9941d89f4 100644 --- a/pages/docs/v11.0.0.js +++ b/pages/docs/v11.0.0.js @@ -1,3 +1,3 @@ -import make from "src/DocsOverview.mjs"; +import make from "src/DocsOverview.mjs" -export default make; +export default make diff --git a/pages/docs/v12.0.0.js b/pages/docs/v12.0.0.js index 8762e51e7..9941d89f4 100644 --- a/pages/docs/v12.0.0.js +++ b/pages/docs/v12.0.0.js @@ -1,3 +1,3 @@ -import make from "src/DocsOverview.mjs"; +import make from "src/DocsOverview.mjs" -export default make; +export default make diff --git a/pages/docs/v8.0.0.js b/pages/docs/v8.0.0.js index 8762e51e7..9941d89f4 100644 --- a/pages/docs/v8.0.0.js +++ b/pages/docs/v8.0.0.js @@ -1,3 +1,3 @@ -import make from "src/DocsOverview.mjs"; +import make from "src/DocsOverview.mjs" -export default make; +export default make diff --git a/pages/docs/v9.0.0.js b/pages/docs/v9.0.0.js index 8762e51e7..9941d89f4 100644 --- a/pages/docs/v9.0.0.js +++ b/pages/docs/v9.0.0.js @@ -1,3 +1,3 @@ -import make from "src/DocsOverview.mjs"; +import make from "src/DocsOverview.mjs" -export default make; +export default make diff --git a/pages/markdown-guide.mdx b/pages/markdown-guide.mdx index a8a727cbb..100f5e384 100644 --- a/pages/markdown-guide.mdx +++ b/pages/markdown-guide.mdx @@ -8,16 +8,14 @@ all available components and demonstrates some use-cases. - ## How does it work? We use a toolset called [mdxjs](https://mdxjs.com) to parse and interpret -`.mdx` files within the `pages/` directory. The default set for our markdown +`.mdx` files within the `pages/` directory. The default set for our markdown components is defined in the `Markdown.default` binding. Each layout in our codebase injects the components via the `Mdx.Provider` component, kinda like this: - ```re let components = Markdown.default; @@ -59,10 +57,8 @@ If you leave them out, the content will be a **plain string** without further fo Keep this in mind when editing content. - ## Text Components - ### Info / Blockquote The `` component is useful for putting notes into a highlighted @@ -84,7 +80,6 @@ section. You can use it via JSX syntax... The `Info` component is also really useful if you want its children to be parsed as markdown. You can even pass in html elements. - ``` @@ -115,7 +110,6 @@ similar information. - ### UrlBox ``` @@ -128,7 +122,9 @@ This is how you define a UrlBox **Examples:** -Check out the officially deployed version of this website (via absolute URL) + + Check out the officially deployed version of this website (via absolute URL) + @@ -139,7 +135,6 @@ Try **multiple paragraphs** for instance! - ### Cite @@ -148,7 +143,6 @@ Try **multiple paragraphs** for instance! - ``` @@ -159,8 +153,8 @@ Try **multiple paragraphs** for instance! ### Intro / Chapter Hero -This component is useful to ease the user into the topic. Use it for the first -paragraph and give a quick overview on what the document is about. +This component is useful to ease the user into the topic. Use it for the first +paragraph and give a quick overview on what the document is about. @@ -178,13 +172,11 @@ Your hero text ``` - ## Codeblocks Codeblocks are represented via \`\`\` codefences. Following languages are available: `res`, `re`, `js`, `ml`, `text`, `json`, `sh` - ```res let a = "This is a Reason codeblock" ``` @@ -198,7 +190,7 @@ highlighting To highlight codelines in your code snippet, use the `{range}` meta parameter: -~~~ +```` ```res {1,4-5} let a = 1 @@ -210,7 +202,7 @@ switch a => { | _ => b } ``` -~~~ +```` which will render: @@ -231,7 +223,8 @@ For cases where you want to show a single codeblock with multiple syntaxes, use Make sure to leave a newline between the `` JSX tags, otherwise codeblock children won't be recognized! **Example:** -~~~ + +```` ```res @@ -245,23 +238,23 @@ var highlighted = "yep" ``` -~~~ +```` Renders to following output: - ```res - let a = "Some ReScript code" - switch myValue { +```res +let a = "Some ReScript code" +switch myValue { - } - ``` +} +``` - ```js {3} - var a = "Some JavaScript code"; +```js {3} +var a = "Some JavaScript code" - var highlighted = "Also supports highlighting ranges"; - ``` +var highlighted = "Also supports highlighting ranges" +``` diff --git a/pages/packages.js b/pages/packages.js index 42d9cc1ab..badde2159 100644 --- a/pages/packages.js +++ b/pages/packages.js @@ -3,5 +3,5 @@ import Comp from "src/Packages" export { getStaticProps } from "src/Packages" export default function PackagesPage(props) { - return ; + return } diff --git a/pages/syntax-lookup.js b/pages/syntax-lookup.js index 22cd2e6f4..cfe8d75d8 100644 --- a/pages/syntax-lookup.js +++ b/pages/syntax-lookup.js @@ -1,6 +1,6 @@ -import SyntaxLookupRes from "src/SyntaxLookup.mjs"; +import SyntaxLookupRes from "src/SyntaxLookup.mjs" -export { getStaticProps } from "src/SyntaxLookup.mjs"; +export { getStaticProps } from "src/SyntaxLookup.mjs" export default function SyntaxLookup(props) { return diff --git a/pages/try.js b/pages/try.js index f9bbdf9fc..7d303fff2 100644 --- a/pages/try.js +++ b/pages/try.js @@ -1 +1 @@ -export { getStaticProps, default } from "src/Try.mjs"; +export { getStaticProps, default } from "src/Try.mjs" diff --git a/plugins/cm-reason-mode.js b/plugins/cm-reason-mode.js index 368d736eb..608d804e8 100644 --- a/plugins/cm-reason-mode.js +++ b/plugins/cm-reason-mode.js @@ -4,8 +4,8 @@ // Originally derived from the CodeMirror Rust plugin: // https://github.com/codemirror/CodeMirror/blob/master/mode/rust/rust.js -import "codemirror/addon/mode/simple"; -import CodeMirror from "codemirror/lib/codemirror"; +import "codemirror/addon/mode/simple" +import CodeMirror from "codemirror/lib/codemirror" CodeMirror.defineSimpleMode("reason", { start: [ @@ -14,45 +14,48 @@ CodeMirror.defineSimpleMode("reason", { // raw string and raw byte string { regex: /b?r"/, token: "string", next: "string_raw" }, { regex: /b?r#+"/, token: "string", next: "string_raw_hash" }, - { regex: /(\:\s*)(.*)(\s*=)\s/, token: [null, "type-annotation", null]}, + { regex: /(\:\s*)(.*)(\s*=)\s/, token: [null, "type-annotation", null] }, // character { regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, - token: "string-2" + token: "string-2", }, // byte { regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2" }, { - regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/, - token: "number" + regex: + /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/, + token: "number", }, { regex: /(let|type)(\s+rec)?(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, - token: ["keyword", "keyword2", null, "def"] + token: ["keyword", "keyword2", null, "def"], }, { - regex: /(?:switch|module|as|do|else|external|for|if|in|loop|mod|pub|ref|type|while|open|open\!)\b/, - token: "keyword" + regex: + /(?:switch|module|as|do|else|external|for|if|in|loop|mod|pub|ref|type|while|open|open\!)\b/, + token: "keyword", }, { regex: /(?:rec)\b/, - token: "keyword2" + token: "keyword2", }, { regex: /\b(?:char|bool|option|int|string)\b/, - token: "atom" + token: "atom", }, { regex: /\b(?:true|false)\b/, token: "builtin" }, { regex: /\b(fun)(\s+)([a-zA-Z_\|][a-zA-Z0-9_]*)/, - token: ["keyword", null, "def"] + token: ["keyword", null, "def"], }, { regex: /\b([A-Z][a-zA-Z0-9_]*)(\.)/, - token: ["module", null] - }, { + token: ["module", null], + }, + { regex: /\b([A-Z][a-zA-Z0-9_]*)/, - token: ["variant-constructor", null, null, null] + token: ["variant-constructor", null, null, null], }, { regex: /\[.*\]/, token: "decorator" }, { regex: /#!?\[.*\]/, token: "meta" }, @@ -62,26 +65,24 @@ CodeMirror.defineSimpleMode("reason", { { regex: /[a-zA-Z_]\w*!/, token: "variable-3" }, { regex: /[a-zA-Z_]\w*/, token: "variable" }, { regex: /[\{\[\(]/, indent: true }, - { regex: /[\}\]\)]/, dedent: true } - ], - variantConstructor: [ - + { regex: /[\}\]\)]/, dedent: true }, ], + variantConstructor: [], string: [ { regex: /"/, token: "string", next: "start" }, - { regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string" } + { regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string" }, ], string_raw: [ { regex: /"/, token: "string", next: "start" }, - { regex: /[^"]*/, token: "string" } + { regex: /[^"]*/, token: "string" }, ], string_raw_hash: [ { regex: /"#+/, token: "string", next: "start" }, - { regex: /(?:[^"]|"(?!#))*/, token: "string" } + { regex: /(?:[^"]|"(?!#))*/, token: "string" }, ], comment: [ { regex: /.*?\*\//, token: "comment", next: "start" }, - { regex: /.*/, token: "comment" } + { regex: /.*/, token: "comment" }, ], meta: { dontIndentStates: ["comment"], @@ -89,9 +90,9 @@ CodeMirror.defineSimpleMode("reason", { blockCommentStart: "/*", blockCommentEnd: "*/", lineComment: "//", - fold: "brace" - } -}); + fold: "brace", + }, +}) -CodeMirror.defineMIME("text/x-reasonsrc", "reason"); -CodeMirror.defineMIME("text/reason", "reason"); +CodeMirror.defineMIME("text/x-reasonsrc", "reason") +CodeMirror.defineMIME("text/reason", "reason") diff --git a/plugins/cm-rescript-mode.js b/plugins/cm-rescript-mode.js index 008a0f57e..e4df8fe30 100644 --- a/plugins/cm-rescript-mode.js +++ b/plugins/cm-rescript-mode.js @@ -4,9 +4,8 @@ // Originally derived from the CodeMirror Rust plugin: // https://github.com/codemirror/CodeMirror/blob/master/mode/rust/rust.js -import "codemirror/addon/mode/simple"; -import CodeMirror from "codemirror/lib/codemirror"; - +import "codemirror/addon/mode/simple" +import CodeMirror from "codemirror/lib/codemirror" CodeMirror.defineSimpleMode("rescript", { start: [ @@ -17,47 +16,49 @@ CodeMirror.defineSimpleMode("rescript", { //{ regex: /b?r#+"/, token: "string", next: "string_raw_hash" }, // string and byte string { regex: /b?"/, token: "string", next: "string" }, - { regex: /(\:\s*)(.*)(\s*=)\s/, token: [null, "type-annotation", null]}, - // interpolation string + { regex: /(\:\s*)(.*)(\s*=)\s/, token: [null, "type-annotation", null] }, + // interpolation string { regex: /b?`/, token: "string", next: "string_interpolation" }, // character { regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, - token: "string-2" + token: "string-2", }, // byte { regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2" }, { - regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/, - token: "number" + regex: + /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/, + token: "number", }, { regex: /(let|type)(\s+rec)?(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, - token: ["keyword", "keyword2", null, "def"] + token: ["keyword", "keyword2", null, "def"], }, { - regex: /(?:and|as|assert|catch|async|await|constraint|downto|else|exception|export|external|false|for|if|import|in|include|lazy|let|module|mutable|of|open|private|switch|to|true|try|type|when|while|with\!)\b/, - token: "keyword" + regex: + /(?:and|as|assert|catch|async|await|constraint|downto|else|exception|export|external|false|for|if|import|in|include|lazy|let|module|mutable|of|open|private|switch|to|true|try|type|when|while|with\!)\b/, + token: "keyword", }, { regex: /(?:rec|list)\b/, - token: "keyword2" + token: "keyword2", }, { regex: /\b(?:char|bool|option|int|string)\b/, - token: "atom" + token: "atom", }, { regex: /\b(?:true|false)\b/, token: "builtin" }, { regex: /\b([A-Z][a-zA-Z0-9_]*)(\.)/, - token: ["module", null] + token: ["module", null], }, { regex: /\b([A-Z][a-zA-Z0-9_]*)/, - token: ["variant-constructor", null, null, null] + token: ["variant-constructor", null, null, null], }, //polyvar - { regex: /#[a-zA-Z0-9_"]*/, token: "variant-constructor"}, + { regex: /#[a-zA-Z0-9_"]*/, token: "variant-constructor" }, { regex: /@.[\w\.\(\)]*/, token: "decorator" }, { regex: /#!?\[.*\]/, token: "meta" }, { regex: /\/\/.*/, token: "comment" }, @@ -66,34 +67,32 @@ CodeMirror.defineSimpleMode("rescript", { { regex: /[a-zA-Z_]\w*!/, token: "variable-3" }, { regex: /[a-zA-Z_]\w*/, token: "variable" }, { regex: /[\{\[\(]/, indent: true }, - { regex: /[\}\]\)]/, dedent: true } - ], - variantConstructor: [ - + { regex: /[\}\]\)]/, dedent: true }, ], + variantConstructor: [], string: [ { regex: /"/, token: "string", next: "start" }, - { regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string" } + { regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string" }, ], //string_raw: [ - //{ regex: /"/, token: "string", next: "start" }, - //{ regex: /[^"]*/, token: "string" } + //{ regex: /"/, token: "string", next: "start" }, + //{ regex: /[^"]*/, token: "string" } //], //string_raw_hash: [ - //{ regex: /"#+/, token: "string", next: "start" }, - //{ regex: /(?:[^"]|"(?!#))*/, token: "string" } + //{ regex: /"#+/, token: "string", next: "start" }, + //{ regex: /(?:[^"]|"(?!#))*/, token: "string" } //], list: [ { regex: /list{/, token: "keyword2", next: "start" }, - { regex: /[^`]*/, token: "string" } + { regex: /[^`]*/, token: "string" }, ], string_interpolation: [ { regex: /`/, token: "string", next: "start" }, - { regex: /[^`]*/, token: "string" } + { regex: /[^`]*/, token: "string" }, ], comment: [ { regex: /.*?\*\//, token: "comment", next: "start" }, - { regex: /.*/, token: "comment" } + { regex: /.*/, token: "comment" }, ], meta: { dontIndentStates: ["comment"], @@ -101,9 +100,9 @@ CodeMirror.defineSimpleMode("rescript", { blockCommentStart: "/*", blockCommentEnd: "*/", lineComment: "//", - fold: "brace" - } -}); + fold: "brace", + }, +}) -CodeMirror.defineMIME("text/x-reasonsrc", "reason"); -CodeMirror.defineMIME("text/reason", "reason"); +CodeMirror.defineMIME("text/x-reasonsrc", "reason") +CodeMirror.defineMIME("text/reason", "reason") diff --git a/plugins/reason-highlightjs.js b/plugins/reason-highlightjs.js index ec77aee43..d0f5901c5 100644 --- a/plugins/reason-highlightjs.js +++ b/plugins/reason-highlightjs.js @@ -6,53 +6,82 @@ Category: functional // Note: Extracted and adapted from the reason-highlightjs package: // https://github.com/reasonml-editor/reason-highlightjs -export default function(hljs) { - function orReValues(ops){ +export default function (hljs) { + function orReValues(ops) { return ops - .map(function(op) { - return op - .split('') - .map(function(char) { - return '\\' + char; - }) - .join(''); - }) - .join('|'); + .map(function (op) { + return op + .split("") + .map(function (char) { + return "\\" + char + }) + .join("") + }) + .join("|") } // eh why is the $ here - var RE_IDENT = '~?[a-z$_][0-9a-zA-Z$_]*'; - var RE_ATTRIBUTE = '[A-Za-z_][A-Za-z0-9_\\.]*'; - var RE_MODULE_IDENT = '[A-Z$_][0-9a-zA-Z$_]*'; - var RE_CONSTRUCTOR = '([A-Z][0-9a-zA-Z$_]*)|(`[a-zA-Z][0-9a-zA-Z$_]*)'; + var RE_IDENT = "~?[a-z$_][0-9a-zA-Z$_]*" + var RE_ATTRIBUTE = "[A-Za-z_][A-Za-z0-9_\\.]*" + var RE_MODULE_IDENT = "[A-Z$_][0-9a-zA-Z$_]*" + var RE_CONSTRUCTOR = "([A-Z][0-9a-zA-Z$_]*)|(`[a-zA-Z][0-9a-zA-Z$_]*)" - var RE_PARAM_TYPEPARAM = '\'?[a-z$_][0-9a-z$_]*'; - var RE_PARAM_TYPE = '\s*:\s*[a-z$_][0-9a-z$_]*(\(\s*(' + RE_PARAM_TYPEPARAM + '\s*(,' + RE_PARAM_TYPEPARAM + ')*)?\s*\))?'; - var RE_PARAM = RE_IDENT + '(' + RE_PARAM_TYPE + ')?(' + RE_PARAM_TYPE + ')?'; - var RE_OPERATOR = "(" + orReValues(['->', '||', '&&', '++', '**', '+.', '+', '-.', '-', '*.', '*', '/.', '/', '...', '|>', '===', '==', '^', ':=', '!']) + ")"; + var RE_PARAM_TYPEPARAM = "'?[a-z$_][0-9a-z$_]*" + var RE_PARAM_TYPE = + "\s*:\s*[a-z$_][0-9a-z$_]*(\(\s*(" + + RE_PARAM_TYPEPARAM + + "\s*(," + + RE_PARAM_TYPEPARAM + + ")*)?\s*\))?" + var RE_PARAM = RE_IDENT + "(" + RE_PARAM_TYPE + ")?(" + RE_PARAM_TYPE + ")?" + var RE_OPERATOR = + "(" + + orReValues([ + "->", + "||", + "&&", + "++", + "**", + "+.", + "+", + "-.", + "-", + "*.", + "*", + "/.", + "/", + "...", + "|>", + "===", + "==", + "^", + ":=", + "!", + ]) + + ")" var KEYWORDS = { /* https://github.com/facebook/reason/blob/79e67d5334ef181fdb54bd57bd9e7729f9fe46e7/src/reason-parser/reason_lexer.mll#L94-L154 */ keyword: - 'and as assert begin class constraint done downto exception external fun ' + - 'esfun function functor include inherit initializer lazy let pub mutable new nonrec ' + - 'object of open or pri rec then to type val virtual ' + - 'try catch finally do else for if switch while import library export ' + - 'module in raise', + "and as assert begin class constraint done downto exception external fun " + + "esfun function functor include inherit initializer lazy let pub mutable new nonrec " + + "object of open or pri rec then to type val virtual " + + "try catch finally do else for if switch while import library export " + + "module in raise", // not reliable // built_in: // 'array bool bytes char exn|5 float int int32 int64 list lazy_t|5 nativeint|5 ref string unit', - literal: - 'true false' - }; + literal: "true false", + } - const RE_NUMBER = '\\b(0[xX][a-fA-F0-9_]+[Lln]?|' + - '0[oO][0-7_]+[Lln]?|' + - '0[bB][01_]+[Lln]?|' + - '[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)'; + const RE_NUMBER = + "\\b(0[xX][a-fA-F0-9_]+[Lln]?|" + + "0[oO][0-7_]+[Lln]?|" + + "0[bB][01_]+[Lln]?|" + + "[0-9][0-9_]*([Lln]|(\\.[0-9_]*)?([eE][-+]?[0-9_]+)?)?)" const STRING_MODE = { - className: 'string', + className: "string", variants: [ { begin: '"', @@ -61,69 +90,69 @@ export default function(hljs) { }, // {foo|bla|foo} { - begin: '\\{(' + RE_IDENT + ')?\\|', - end: '\\|(' + RE_IDENT + ')?\\}', + begin: "\\{(" + RE_IDENT + ")?\\|", + end: "\\|(" + RE_IDENT + ")?\\}", }, - ] - }; + ], + } const CHARACTER_MODE = { - className: 'character', - begin: '\'(\\\\[^\']+|[^\'])\'', - illegal: '\\n', - relevance: 0 - }; + className: "character", + begin: "'(\\\\[^']+|[^'])'", + illegal: "\\n", + relevance: 0, + } const NUMBER_MODE = { - className: 'number', + className: "number", relevance: 0, variants: [ { - begin: RE_NUMBER + begin: RE_NUMBER, }, { - begin: '\\(\\-' + RE_NUMBER + '\\)' - } - ] - }; + begin: "\\(\\-" + RE_NUMBER + "\\)", + }, + ], + } const OPERATOR_MODE = { - className: 'operator', + className: "operator", relevance: 0, - begin: RE_OPERATOR - }; + begin: RE_OPERATOR, + } // as in variant constructor const CONSTRUCTOR_MODE = { - className: 'constructor', - begin: '\\b' + RE_CONSTRUCTOR, - illegal: '\\n', + className: "constructor", + begin: "\\b" + RE_CONSTRUCTOR, + illegal: "\\n", keywords: KEYWORDS, - }; + } const ARRAY_MODES = { - className: 'literal', + className: "literal", variants: [ { - begin: '\\[\\|', + begin: "\\[\\|", }, { - begin: '\\|\\]', + begin: "\\|\\]", }, - ] - }; + ], + } const LIST_MODES = { - className: 'literal', + className: "literal", variants: [ { - begin: '\\[', + begin: "\\[", }, { - begin: '\\]', + begin: "\\]", }, - ] - }; + ], + } const MODULE_ACCESS_MODE = { begin: "\\b" + RE_MODULE_IDENT + "\\.", @@ -131,10 +160,10 @@ export default function(hljs) { contains: [ { begin: RE_MODULE_IDENT, - className: 'module-identifier', + className: "module-identifier", }, - ] - }; + ], + } const JSX_MODE = { variants: [ @@ -152,21 +181,21 @@ export default function(hljs) { contains: [ { begin: RE_MODULE_IDENT, - className: 'module-identifier', + className: "module-identifier", }, - ] + ], }, { begin: "<", contains: [ { begin: RE_MODULE_IDENT, - className: 'module-identifier', + className: "module-identifier", }, - ] + ], }, - ] - }; + ], + } // Foo.Bar.Baz where Baz is actually a module, not a constructor const MODULE_ACCESS_ENDS_WITH_MODULE = { @@ -183,13 +212,13 @@ export default function(hljs) { { begin: RE_MODULE_IDENT, className: "module-identifier", - } - ] + }, + ], }, - ] - }; + ], + } const ATTRIBUTE_MODE = { - className: 'attribute', + className: "attribute", variants: [ { begin: "\\[@", @@ -227,24 +256,22 @@ export default function(hljs) { }, ], }, - ] - }; + ], + } // all the modes below are mutually recursive let OPEN_OR_INCLUDE_MODULE_MODE = { begin: "\\b(open|include)\\s*", keywords: KEYWORDS, - contains: [ - MODULE_ACCESS_ENDS_WITH_MODULE, - ] - }; + contains: [MODULE_ACCESS_ENDS_WITH_MODULE], + } let MODULE_MODE = { begin: "\\s*\\{\\s*", end: "\\s*\\}\\s*", keywords: KEYWORDS, // most of the order here is important contains: [ - hljs.COMMENT('/\\*', '\\*/', { illegal: '^(\\#,\\/\\/)' }), + hljs.COMMENT("/\\*", "\\*/", { illegal: "^(\\#,\\/\\/)" }), hljs.C_LINE_COMMENT_MODE, // there's also a block mode technically, but for our purpose, a module {} // and a block {} can be considered the same for highlighting @@ -259,8 +286,8 @@ export default function(hljs) { OPEN_OR_INCLUDE_MODULE_MODE, MODULE_ACCESS_MODE, CONSTRUCTOR_MODE, - ] - }; + ], + } const MODULE_DECLARATION_MODE = { begin: "\\bmodule\\s+(type\\s+)?(of\\s+)?", keywords: KEYWORDS, @@ -279,7 +306,7 @@ export default function(hljs) { begin: RE_MODULE_IDENT, className: "module-identifier", }, - MODULE_MODE + MODULE_MODE, ], }, // then the = part and the right hand side @@ -308,29 +335,28 @@ export default function(hljs) { MODULE_MODE, { begin: "\\s*,\\s*", - } - ] + }, + ], }, MODULE_MODE, - ] + ], }, MODULE_MODE, { - begin: "\\s*=>\\s*" - } - ] + begin: "\\s*=>\\s*", + }, + ], }, - ] - }; - MODULE_MODE.contains.unshift(MODULE_DECLARATION_MODE); + ], + } + MODULE_MODE.contains.unshift(MODULE_DECLARATION_MODE) OPEN_OR_INCLUDE_MODULE_MODE.contains.push(MODULE_MODE) return { - aliases: ['re', 'reasonml', 'rei'], + aliases: ["re", "reasonml", "rei"], keywords: KEYWORDS, - illegal: '(:\\-|:=|\\${|\\+=)', + illegal: "(:\\-|:=|\\${|\\+=)", // lol beautiful contains: MODULE_MODE.contains, - }; + } } - diff --git a/public/static/docson/box.html b/public/static/docson/box.html index c39369d69..a6ed00877 100644 --- a/public/static/docson/box.html +++ b/public/static/docson/box.html @@ -1,157 +1,98 @@ -{{! - Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -}} -{{#scope this}} -
-
-
- {{#name this}}
{{__name}}
{{/name}} -
{{desc this}}
-
+{{! Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch) Licensed under +the Apache License, Version 2.0 (the "License"); you may not use this file +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. }} {{#scope this}} +
+
+
+ {{#name this}} +
{{__name}}
+ {{/name}} +
{{desc this}}
+
+
+
+
{}
+
+
+
+ {{#sub this}} +
+
+
+ {{#primitive this}} {{signature this null this}} {{/primitive}} + {{#equals type "array"}} {{signature this "array" items}} {{/equals}} + {{#if allOf}} {{signature this "all of" allOf}} {{/if}} {{#if anyOf}} + {{signature this "any of" anyOf}} {{/if}} {{#if oneOf}} {{signature + this "one of" oneOf}} {{/if}} {{#if not}} {{signature this "not" not}} + {{/if}}
+
+
+ {{#boxes}} +
{{schema this}}
+ {{/boxes}} +
-
{}
-
-
+
- {{#sub this}} -
-
-
- {{#primitive this}} - {{signature this null this}} - {{/primitive}} - {{#equals type "array"}} - {{signature this "array" items}} - {{/equals}} - {{#if allOf}} - {{signature this "all of" allOf}} - {{/if}} - {{#if anyOf}} - {{signature this "any of" anyOf}} - {{/if}} - {{#if oneOf}} - {{signature this "one of" oneOf}} - {{/if}} - {{#if not}} - {{signature this "not" not}} - {{/if}} -
-
-
- {{#boxes}} -
- {{schema this}} -
- {{/boxes}} -
+ {{/sub}} {{#if properties}} {{#each properties}} +
+
+
+ {{@key}} +
+
+ {{#main this}} {{signature this null this}} {{/main}} {{#equals type + "array"}} {{signature this "array" items}} {{/equals}} {{#if allOf}} + {{signature this "all of" allOf}} {{/if}} {{#if anyOf}} {{signature + this "any of" anyOf}} {{/if}} {{#if oneOf}} {{signature this "one of" + oneOf}} {{/if}} {{#if additionalProperties}} {{signature this "map" + additionalProperties}} {{/if}} {{#if not}} {{signature this "not" + not}} {{/if}}
- {{/sub}} - {{#if properties}} - {{#each properties}} -
-
-
{{@key}}
-
- {{#main this}} - {{signature this null this}} - {{/main}} - {{#equals type "array"}} - {{signature this "array" items}} - {{/equals}} - {{#if allOf}} - {{signature this "all of" allOf}} - {{/if}} - {{#if anyOf}} - {{signature this "any of" anyOf}} - {{/if}} - {{#if oneOf}} - {{signature this "one of" oneOf}} - {{/if}} - {{#if additionalProperties}} - {{signature this "map" additionalProperties}} - {{/if}} - {{#if not}} - {{signature this "not" not}} - {{/if}} -
-
{{desc this}}
-
-
- {{#boxes}} -
- {{schema this}} -
- {{/boxes}} -
-
- {{/each}} - {{/if}} - - {{#if patternProperties}} - {{#each patternProperties}} -
-
-
{{@key}}
-
- {{#main this}} - {{signature this null this}} - {{/main}} - {{#equals type "array"}} - {{signature this "array" items}} - {{/equals}} - {{#if allOf}} - {{signature this "all of" allOf}} - {{/if}} - {{#if anyOf}} - {{signature this "any of" anyOf}} - {{/if}} - {{#if oneOf}} - {{signature this "one of" oneOf}} - {{/if}} - {{#if not}} - {{signature this "not" not}} - {{/if}} -
-
{{desc this}}
-
-
- {{#boxes}} -
- {{schema this}} -
- {{/boxes}} -
-
- {{/each}} - {{/if}} - - {{#if additionalProperties}} -
-
-
additional
-
- {{schema ../additionalProperties}} -
-
+
{{desc this}}
+
+
+ {{#boxes}} +
{{schema this}}
+ {{/boxes}} +
+
+ {{/each}} {{/if}} {{#if patternProperties}} {{#each patternProperties}} +
+
+
{{@key}}
+
+ {{#main this}} {{signature this null this}} {{/main}} {{#equals type + "array"}} {{signature this "array" items}} {{/equals}} {{#if allOf}} + {{signature this "all of" allOf}} {{/if}} {{#if anyOf}} {{signature + this "any of" anyOf}} {{/if}} {{#if oneOf}} {{signature this "one of" + oneOf}} {{/if}} {{#if not}} {{signature this "not" not}} {{/if}}
- {{/if}} +
{{desc this}}
+
+
+ {{#boxes}} +
{{schema this}}
+ {{/boxes}} +
-
-
-{{source this}}
+ {{/each}} {{/if}} {{#if additionalProperties}} +
+
+
additional
+
{{schema ../additionalProperties}}
+
+ {{/if}} +
+
+
{{source this}}
+
-
+
{{/scope}} diff --git a/public/static/docson/signature.html b/public/static/docson/signature.html index 8412dbc23..75672360a 100644 --- a/public/static/docson/signature.html +++ b/public/static/docson/signature.html @@ -1,86 +1,67 @@ -{{! - Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch) - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -}} -{{#if keyword}} - {{keyword}}{{range schema.minItems schema.maxItems 0 "" false false ".."}} - {{#each schemas}} - {{enum this}} - {{#simple this}} - - {{__type}} - - - {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}} - - {{/simple}} - {{#complex this}} - {{#box this}} - {{__type}} - {{/box}} - {{/complex}} - {{#ref this}} - {{#box this}} - {{__name}} - {{/box}} - {{/ref}} - {{#if pattern}} - /{{pattern}}/ - {{/if}} - {{#if enum}} - {{#each enum}} - {{this}} - {{/each}} - {{/if}} - {{#exists default}} - default - {{__default}} - {{/exists}} - {{/each}} -{{/if}} -{{#unless keyword}} - {{#each schemas}} - {{enum this}} - {{#primitive this}} - - {{__type}} - - - {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}} - - {{/primitive}} - {{#ref this}} - {{#box this}} - {{__name}} - {{/box}} - {{/ref}} - {{#obj this}} - {{#box this}} - {{__type}} - {{/box}} - {{/obj}} - {{#if pattern}} - /{{pattern}}/ - {{/if}} - {{#if enum}} - {{#each enum}} - {{this}} - {{/each}} - {{/if}} - {{#exists default}} - default - {{__default}} - {{/exists}} - {{/each}} -{{/unless}} +{{! Copyright 2013 Laurent Bovet (laurent.bovet@windmaster.ch) Licensed under +the Apache License, Version 2.0 (the "License"); you may not use this file +except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. }} {{#if keyword}} +{{keyword}}{{range schema.minItems schema.maxItems 0 "" false false + ".."}} +{{#each schemas}} +{{enum this}} +{{#simple this}} + {{__type}} + + {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum + "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}} + +{{/simple}} {{#complex this}} {{#box this}} +{{__type}} +{{/box}} {{/complex}} {{#ref this}} {{#box this}} +{{__name}} +{{/box}} {{/ref}} {{#if pattern}} +/{{pattern}}/ +{{/if}} {{#if enum}} {{#each enum}} +{{this}} +{{/each}} {{/if}} {{#exists default}} +default +{{__default}} +{{/exists}} {{/each}} {{/if}} {{#unless keyword}} {{#each schemas}} +{{enum this}} +{{#primitive this}} + {{__type}} + + {{range minLength maxLength "" "" false false ".."}}{{range minimum maximum + "-∞" "∞" exclusiveMinimum exclusiveMaximum ";"}} + +{{/primitive}} {{#ref this}} {{#box this}} +{{__name}} +{{/box}} {{/ref}} {{#obj this}} {{#box this}} +{{__type}} +{{/box}} {{/obj}} {{#if pattern}} +/{{pattern}}/ +{{/if}} {{#if enum}} {{#each enum}} +{{this}} +{{/each}} {{/if}} {{#exists default}} +default +{{__default}} +{{/exists}} {{/each}} {{/unless}} diff --git a/public/static/favicon/site.webmanifest b/public/static/favicon/site.webmanifest index 45dc8a206..fa99de77d 100644 --- a/public/static/favicon/site.webmanifest +++ b/public/static/favicon/site.webmanifest @@ -1 +1,19 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/rescript.json b/rescript.json index 87790843e..84bf239bd 100644 --- a/rescript.json +++ b/rescript.json @@ -4,13 +4,8 @@ "jsx": { "version": 4 }, - "bs-dependencies": [ - "@rescript/react", - "@rescript/webapi" - ], - "bsc-flags": [ - "-open WebAPI.Global" - ], + "bs-dependencies": ["@rescript/react", "@rescript/webapi"], + "bsc-flags": ["-open WebAPI.Global"], "sources": [ { "dir": "src", diff --git a/scripts/bsb-benchmark-test.js b/scripts/bsb-benchmark-test.js index f73e6242e..bbdc3df9a 100644 --- a/scripts/bsb-benchmark-test.js +++ b/scripts/bsb-benchmark-test.js @@ -19,105 +19,104 @@ * to NodeJS > v12 */ -const path = require("path"); -const child_process = require("child_process"); -const bsconfig = require(path.join(process.cwd(), "bsconfig.json")); +const path = require("path") +const child_process = require("child_process") +const bsconfig = require(path.join(process.cwd(), "bsconfig.json")) -const BSB_BIN = "./node_modules/.bin/bsb"; +const BSB_BIN = "./node_modules/.bin/bsb" -const bsbClean = () => child_process.spawnSync(BSB_BIN, ["-clean"]); +const bsbClean = () => child_process.spawnSync(BSB_BIN, ["-clean"]) // returns time { real, user, sys } const timeBsbBuild = () => { - const ret = child_process.spawnSync("time", [BSB_BIN]); + const ret = child_process.spawnSync("time", [BSB_BIN]) if (ret.status !== 0) { throw new Error(`bsb build failed with exit code ${ret.status} fix the build issue before benchmarking - `); + `) } // return output of the time function - const output = ret.stderr.toString(); - const match = output.match(/\s+(.*)\sreal\s*(.*)\suser\s*(.*)\ssys.*/); + const output = ret.stderr.toString() + const match = output.match(/\s+(.*)\sreal\s*(.*)\suser\s*(.*)\ssys.*/) - let time; + let time if (match) { time = { real: parseFloat(match[1]), user: parseFloat(match[2]), - sys: parseFloat(match[3]) - }; + sys: parseFloat(match[3]), + } } - return time; -}; + return time +} const getFileMetrics = () => { - const paths = bsconfig.sources.map(source => { + const paths = bsconfig.sources.map((source) => { if (typeof source === "string") { - return source; + return source } if (typeof source === "object") { - return source.dir; + return source.dir } - }); + }) - const args = paths.concat(["--include-lang=ReasonML", "--json"]); - const ret = child_process.spawnSync("cloc", args); + const args = paths.concat(["--include-lang=ReasonML", "--json"]) + const ret = child_process.spawnSync("cloc", args) if (ret.status !== 0) { - throw new Error(`cloc failed with exit code ${ret.status}`); + throw new Error(`cloc failed with exit code ${ret.status}`) } - const out = JSON.parse(ret.stdout.toString()); + const out = JSON.parse(ret.stdout.toString()) return { numberOfFiles: out.ReasonML.nFiles, blankLines: out.ReasonML.blank, commentLines: out.ReasonML.comment, codeLines: out.ReasonML.code, - totalLines: out.header.n_lines - }; -}; + totalLines: out.header.n_lines, + } +} function main() { - const arg = process.argv[2]; + const arg = process.argv[2] if (arg === "--help" || arg === "-h") { - console.log("Runs simple benchmarks for a BuckleScript project"); - return; + console.log("Runs simple benchmarks for a BuckleScript project") + return } - let asJson = true; + let asJson = true - console.error("Capturing LOC / file numbers..."); - const fileMetrics = getFileMetrics(); + console.error("Capturing LOC / file numbers...") + const fileMetrics = getFileMetrics() - console.error("Cleaning the project first..."); - bsbClean(); + console.error("Cleaning the project first...") + bsbClean() - console.error("Measuring build performance..."); - let buildTime = timeBsbBuild(); + console.error("Measuring build performance...") + let buildTime = timeBsbBuild() - let locPerSec = Math.round(fileMetrics.totalLines / buildTime.real); + let locPerSec = Math.round(fileMetrics.totalLines / buildTime.real) let result = { fileMetrics, buildTime, results: { - locPerSec - } - }; + locPerSec, + }, + } if (asJson) { - console.log(JSON.stringify(result, null, 2)); + console.log(JSON.stringify(result, null, 2)) } - // TODO: maybe add human readable format as an option? } try { - main(); + main() } catch (err) { - console.error("Something went wrong: " + err); - process.exit(1); + console.error("Something went wrong: " + err) + process.exit(1) } diff --git a/scripts/extract-indices.mjs b/scripts/extract-indices.mjs index d6d05777f..a713471da 100644 --- a/scripts/extract-indices.mjs +++ b/scripts/extract-indices.mjs @@ -5,99 +5,105 @@ * - Module names (h1) * - function names (```res sig) */ -import glob from "glob"; -import path from "path"; -import fs from "fs"; -import { URL } from 'url'; +import glob from "glob" +import path from "path" +import fs from "fs" +import { URL } from "url" -import { defaultProcessor } from "./markdown.js"; +import { defaultProcessor } from "./markdown.js" -const pathname = new URL('.', import.meta.url).pathname; -const __dirname = process.platform !== 'win32' ? pathname : pathname.substring(1) +const pathname = new URL(".", import.meta.url).pathname +const __dirname = + process.platform !== "win32" ? pathname : pathname.substring(1) -const processFile = filepath => { - const content = fs.readFileSync(filepath, "utf8"); - const result = defaultProcessor.processSync(content); +const processFile = (filepath) => { + const content = fs.readFileSync(filepath, "utf8") + const result = defaultProcessor.processSync(content) - const pagesPath = path.resolve("./pages"); - const relFilepath = path.relative(pagesPath, filepath); - const parsedPath = path.parse(relFilepath); + const pagesPath = path.resolve("./pages") + const relFilepath = path.relative(pagesPath, filepath) + const parsedPath = path.parse(relFilepath) const dataset = { headers: result.data.headers, signatures: result.data.codeblocks.re, href: path.join(parsedPath.dir, parsedPath.name), - moduleName: result.data.mainHeader - }; - return dataset; -}; + moduleName: result.data.mainHeader, + } + return dataset +} -const createIndex = result => { +const createIndex = (result) => { // Currently we reorder the data to a map, the key is // reflected as the router pathname, as defined by the // NextJS router return result.reduce((acc, data) => { - const { signatures = [], moduleName, headers } = data; + const { signatures = [], moduleName, headers } = data // Sort the headers, but keep type `t` as the first header. - const headersSorted = [...headers].sort( - (headerA, headerB) => { - if (headerA.name === 't') { - return -1 - } else if (headerB.name === 't') { - return 1 - } else { - return headerA.name.localeCompare(headerB.name) - } + const headersSorted = [...headers].sort((headerA, headerB) => { + if (headerA.name === "t") { + return -1 + } else if (headerB.name === "t") { + return 1 + } else { + return headerA.name.localeCompare(headerB.name) } - ); + }) acc["/" + data.href] = { moduleName, - headers: headersSorted - }; + headers: headersSorted, + } - return acc; - }, {}); -}; + return acc + }, {}) +} -const extractApiIndex = version => { - const versionLabel = version.replace(/\./g, ""); - const VERSION_DIR = path.join(__dirname, "../pages/docs/manual", version, "api"); - const BELT_MD_DIR = path.join(VERSION_DIR, "belt"); +const extractApiIndex = (version) => { + const versionLabel = version.replace(/\./g, "") + const VERSION_DIR = path.join( + __dirname, + "../pages/docs/manual", + version, + "api", + ) + const BELT_MD_DIR = path.join(VERSION_DIR, "belt") const BELT_INDEX_FILE = path.join( __dirname, - `../index_data/${versionLabel}_belt_api_index.json` - ); - const beltFiles = glob.sync(`${BELT_MD_DIR}/*.md?(x)`); - const beltResult = beltFiles.map(processFile); - const beltIndex = createIndex(beltResult); - - fs.writeFileSync(BELT_INDEX_FILE, JSON.stringify(beltIndex), "utf8"); + `../index_data/${versionLabel}_belt_api_index.json`, + ) + const beltFiles = glob.sync(`${BELT_MD_DIR}/*.md?(x)`) + const beltResult = beltFiles.map(processFile) + const beltIndex = createIndex(beltResult) - const JS_MD_DIR = path.join(VERSION_DIR, "js"); - const JS_INDEX_FILE = path.join(__dirname, `../index_data/${versionLabel}_js_api_index.json`); - const jsFiles = glob.sync(`${JS_MD_DIR}/*.md?(x)`); - const jsResult = jsFiles.map(processFile); - const jsIndex = createIndex(jsResult); - fs.writeFileSync(JS_INDEX_FILE, JSON.stringify(jsIndex), "utf8"); + fs.writeFileSync(BELT_INDEX_FILE, JSON.stringify(beltIndex), "utf8") - const DOM_MD_DIR = path.join(VERSION_DIR, "dom"); + const JS_MD_DIR = path.join(VERSION_DIR, "js") + const JS_INDEX_FILE = path.join( + __dirname, + `../index_data/${versionLabel}_js_api_index.json`, + ) + const jsFiles = glob.sync(`${JS_MD_DIR}/*.md?(x)`) + const jsResult = jsFiles.map(processFile) + const jsIndex = createIndex(jsResult) + fs.writeFileSync(JS_INDEX_FILE, JSON.stringify(jsIndex), "utf8") + + const DOM_MD_DIR = path.join(VERSION_DIR, "dom") const DOM_INDEX_FILE = path.join( __dirname, - `../index_data/${versionLabel}_dom_api_index.json` - ); + `../index_data/${versionLabel}_dom_api_index.json`, + ) const domFiles = glob .sync(`${DOM_MD_DIR}/*.md?(x)`) - .concat(DOM_MD_DIR + ".mdx"); - const domResult = domFiles.map(processFile); - const domIndex = createIndex(domResult); + .concat(DOM_MD_DIR + ".mdx") + const domResult = domFiles.map(processFile) + const domIndex = createIndex(domResult) - fs.writeFileSync(DOM_INDEX_FILE, JSON.stringify(domIndex), "utf8"); -}; + fs.writeFileSync(DOM_INDEX_FILE, JSON.stringify(domIndex), "utf8") +} // For versions > 10, please use gendocs.res -extractApiIndex("v10.0.0"); -extractApiIndex("v9.0.0"); -extractApiIndex("v8.0.0"); - +extractApiIndex("v10.0.0") +extractApiIndex("v9.0.0") +extractApiIndex("v8.0.0") diff --git a/scripts/extract-syntax.mjs b/scripts/extract-syntax.mjs index 86aa85cfe..72fbfb17a 100644 --- a/scripts/extract-syntax.mjs +++ b/scripts/extract-syntax.mjs @@ -1,42 +1,57 @@ -import glob from "glob"; -import path from "path"; -import fs from "fs"; -import { URL } from 'url'; - -import { defaultProcessor } from "./markdown.js"; - -const pathname = new URL('.', import.meta.url).pathname; -const __dirname = process.platform !== 'win32' ? pathname : pathname.substring(1) - -const processFile = filepath => { - const content = fs.readFileSync(filepath, "utf8"); - const { data: { matter } } = defaultProcessor.processSync(content); - - const syntaxPath = path.resolve("./misc_docs/syntax"); - const relFilePath = path.relative(syntaxPath, filepath); - const parsedPath = path.parse(relFilePath); - - if (matter.id && matter.keywords && matter.name && matter.summary && matter.category) { +import glob from "glob" +import path from "path" +import fs from "fs" +import { URL } from "url" + +import { defaultProcessor } from "./markdown.js" + +const pathname = new URL(".", import.meta.url).pathname +const __dirname = + process.platform !== "win32" ? pathname : pathname.substring(1) + +const processFile = (filepath) => { + const content = fs.readFileSync(filepath, "utf8") + const { + data: { matter }, + } = defaultProcessor.processSync(content) + + const syntaxPath = path.resolve("./misc_docs/syntax") + const relFilePath = path.relative(syntaxPath, filepath) + const parsedPath = path.parse(relFilePath) + + if ( + matter.id && + matter.keywords && + matter.name && + matter.summary && + matter.category + ) { return { file: parsedPath.name, id: matter.id, keywords: matter.keywords, name: matter.name, summary: matter.summary, - category: matter.category + category: matter.category, } } console.error("Metadata missing in " + parsedPath.name + ".mdx") - return null; -}; - -const extractSyntax = async version => { - const SYNTAX_MD_DIR = path.join(__dirname, "../misc_docs/syntax"); - const SYNTAX_INDEX_FILE = path.join(__dirname, "../index_data/syntax_index.json"); - const syntaxFiles = glob.sync(`${SYNTAX_MD_DIR}/*.md?(x)`); - const syntaxIndex = syntaxFiles.map(processFile).filter(Boolean).sort((a, b) => a.name.localeCompare(b.name)) - fs.writeFileSync(SYNTAX_INDEX_FILE, JSON.stringify(syntaxIndex), "utf8"); -}; + return null +} + +const extractSyntax = async (version) => { + const SYNTAX_MD_DIR = path.join(__dirname, "../misc_docs/syntax") + const SYNTAX_INDEX_FILE = path.join( + __dirname, + "../index_data/syntax_index.json", + ) + const syntaxFiles = glob.sync(`${SYNTAX_MD_DIR}/*.md?(x)`) + const syntaxIndex = syntaxFiles + .map(processFile) + .filter(Boolean) + .sort((a, b) => a.name.localeCompare(b.name)) + fs.writeFileSync(SYNTAX_INDEX_FILE, JSON.stringify(syntaxIndex), "utf8") +} extractSyntax() diff --git a/scripts/extract-tocs.mjs b/scripts/extract-tocs.mjs index 00055a9f7..d686f022e 100644 --- a/scripts/extract-tocs.mjs +++ b/scripts/extract-tocs.mjs @@ -2,29 +2,29 @@ * This script is used for generating the table of contents for prose * text documents */ -import { unified } from "unified"; -import glob from "glob"; -import path from "path"; -import fs from "fs"; -import { URL } from "url"; +import { unified } from "unified" +import glob from "glob" +import path from "path" +import fs from "fs" +import { URL } from "url" -import { defaultProcessor } from "./markdown.js"; +import { defaultProcessor } from "./markdown.js" -const pathname = new URL(".", import.meta.url).pathname; +const pathname = new URL(".", import.meta.url).pathname const __dirname = - process.platform !== "win32" ? pathname : pathname.substring(1); + process.platform !== "win32" ? pathname : pathname.substring(1) // orderArr: ["introduction", "overview",,...] const orderFiles = (filepaths, orderArr) => { const order = orderArr.reduce((acc, next, i) => { - acc[next] = null; - return acc; - }, {}); + acc[next] = null + return acc + }, {}) filepaths.forEach((filepath) => { - const id = path.basename(filepath, path.extname(filepath)); - order[id] = filepath; - }); + const id = path.basename(filepath, path.extname(filepath)) + order[id] = filepath + }) // last sanity check if there's an unmatched filepath Object.entries(order).forEach(([name, filepath]) => { @@ -32,46 +32,46 @@ const orderFiles = (filepaths, orderArr) => { // sidebar json if (filepath == null) { throw new Error( - `Cannot find file for "${name}". Does it exist in the pages folder?` - ); + `Cannot find file for "${name}". Does it exist in the pages folder?`, + ) } - }); + }) - return Object.values(order); -}; + return Object.values(order) +} // Used for collapsing nested inlineCodes with the actual header text const collapseHeaderChildren = (children) => { return children.reduce((acc, node) => { if (node.type === "link") { - return acc + collapseHeaderChildren(node.children); + return acc + collapseHeaderChildren(node.children) } // Prevents 'undefined' values in our headers else if (node.value == null) { - return acc; + return acc } - return acc + node.value; - }, ""); -}; + return acc + node.value + }, "") +} // sidebarJson: { [category: string]: array } const processFile = (filepath, sidebarJson = {}) => { - const content = fs.readFileSync(filepath, "utf8"); - const result = defaultProcessor.processSync(content); - const data = result.data.matter; + const content = fs.readFileSync(filepath, "utf8") + const result = defaultProcessor.processSync(content) + const data = result.data.matter - const pagesPath = path.resolve("./pages"); - const relFilepath = path.relative(pagesPath, filepath); - const parsedPath = path.parse(relFilepath); - const filename = path.basename(filepath, path.extname(filepath)); + const pagesPath = path.resolve("./pages") + const relFilepath = path.relative(pagesPath, filepath) + const parsedPath = path.parse(relFilepath) + const filename = path.basename(filepath, path.extname(filepath)) - const title = data.title || result.data.mainHeader || filename; + const title = data.title || result.data.mainHeader || filename - let category; + let category for (const [categoryName, items] of Object.entries(sidebarJson)) { if (items.find((item) => filename === item)) { - category = categoryName; - break; + category = categoryName + break } } @@ -80,106 +80,106 @@ const processFile = (filepath, sidebarJson = {}) => { headers: result.data.headers, href: path.join(parsedPath.dir, parsedPath.name), title, - }; + } if (category != null) { - dataset.category = category; + dataset.category = category } - return dataset; -}; + return dataset +} const createTOC = (result) => { // Currently we reorder the data to a map, the key is // reflected as the router pathname, as defined by the // NextJS router return result.reduce((acc, data) => { - const { title, headers, category, id } = data; + const { title, headers, category, id } = data acc["/" + data.href] = { id, title, headers, category, - }; + } - return acc; - }, {}); -}; + return acc + }, {}) +} const createManualToc = (version) => { - const versionNoDot = version.replaceAll(".", ""); - const MD_DIR = path.join(__dirname, `../pages/docs/manual/${version}`); + const versionNoDot = version.replaceAll(".", "") + const MD_DIR = path.join(__dirname, `../pages/docs/manual/${version}`) const SIDEBAR_JSON = path.join( __dirname, - `../data/sidebar_manual_${versionNoDot}.json` - ); + `../data/sidebar_manual_${versionNoDot}.json`, + ) const TARGET_FILE = path.join( __dirname, - `../index_data/manual_${versionNoDot}_toc.json` - ); + `../index_data/manual_${versionNoDot}_toc.json`, + ) - const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)); + const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)) const FILE_ORDER = Object.values(sidebarJson).reduce((acc, items) => { - return acc.concat(items); - }, []); + return acc.concat(items) + }, []) - const files = glob.sync(`${MD_DIR}/*.?(js|md?(x))`); - const ordered = orderFiles(files, FILE_ORDER); + const files = glob.sync(`${MD_DIR}/*.?(js|md?(x))`) + const ordered = orderFiles(files, FILE_ORDER) - const result = ordered.map((filepath) => processFile(filepath, sidebarJson)); - const toc = createTOC(result); + const result = ordered.map((filepath) => processFile(filepath, sidebarJson)) + const toc = createTOC(result) - fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8"); -}; + fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8") +} const createReactToc = (version) => { - const versionLabel = version.replace(/\./g, ""); - const MD_DIR = path.join(__dirname, "../pages/docs/react"); + const versionLabel = version.replace(/\./g, "") + const MD_DIR = path.join(__dirname, "../pages/docs/react") const SIDEBAR_JSON = path.join( __dirname, - `../data/sidebar_react_${versionLabel}.json` - ); + `../data/sidebar_react_${versionLabel}.json`, + ) const TARGET_FILE = path.join( __dirname, - `../index_data/react_${versionLabel}_toc.json` - ); + `../index_data/react_${versionLabel}_toc.json`, + ) - const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)); + const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)) const FILE_ORDER = Object.values(sidebarJson).reduce((acc, items) => { - return acc.concat(items); - }, []); + return acc.concat(items) + }, []) - const files = glob.sync(`${MD_DIR}/${version}/*.md?(x)`); - const ordered = orderFiles(files, FILE_ORDER); + const files = glob.sync(`${MD_DIR}/${version}/*.md?(x)`) + const ordered = orderFiles(files, FILE_ORDER) - const result = ordered.map((filepath) => processFile(filepath, sidebarJson)); - const toc = createTOC(result); + const result = ordered.map((filepath) => processFile(filepath, sidebarJson)) + const toc = createTOC(result) - fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8"); -}; + fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8") +} const createCommunityToc = () => { - const MD_DIR = path.join(__dirname, "../pages/community"); - const SIDEBAR_JSON = path.join(__dirname, "../data/sidebar_community.json"); - const TARGET_FILE = path.join(__dirname, "../index_data/community_toc.json"); + const MD_DIR = path.join(__dirname, "../pages/community") + const SIDEBAR_JSON = path.join(__dirname, "../data/sidebar_community.json") + const TARGET_FILE = path.join(__dirname, "../index_data/community_toc.json") - const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)); + const sidebarJson = JSON.parse(fs.readFileSync(SIDEBAR_JSON)) const FILE_ORDER = Object.values(sidebarJson).reduce((acc, items) => { - return acc.concat(items); - }, []); + return acc.concat(items) + }, []) - const files = glob.sync(`${MD_DIR}/*.?(js|md?(x))`); - const ordered = orderFiles(files, FILE_ORDER); + const files = glob.sync(`${MD_DIR}/*.?(js|md?(x))`) + const ordered = orderFiles(files, FILE_ORDER) - const result = ordered.map((filepath) => processFile(filepath, sidebarJson)); - const toc = createTOC(result); + const result = ordered.map((filepath) => processFile(filepath, sidebarJson)) + const toc = createTOC(result) - fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8"); -}; + fs.writeFileSync(TARGET_FILE, JSON.stringify(toc), "utf8") +} /* const debugToc = () => { @@ -196,9 +196,9 @@ const debugToc = () => { debugToc(); */ -let manualVersions = ["v12.0.0", "v11.0.0", "v10.0.0", "v9.0.0", "v8.0.0"]; -let reactManualVersions = ["latest", "v0.10.0", "v0.11.0"]; +let manualVersions = ["v12.0.0", "v11.0.0", "v10.0.0", "v9.0.0", "v8.0.0"] +let reactManualVersions = ["latest", "v0.10.0", "v0.11.0"] -manualVersions.forEach(createManualToc); -reactManualVersions.forEach(createReactToc); -createCommunityToc(); +manualVersions.forEach(createManualToc) +reactManualVersions.forEach(createReactToc) +createCommunityToc() diff --git a/scripts/figma-fetch.js b/scripts/figma-fetch.js index cbfbcefa7..85bab31b3 100644 --- a/scripts/figma-fetch.js +++ b/scripts/figma-fetch.js @@ -16,95 +16,93 @@ */ // Inspect the /files JSON response, or the URL of the Figma page: // https://www.figma.com/file//Some-Name?node-id= -const PAGE_ID = '312:2'; +const PAGE_ID = "312:2" // Go to a team URL and get the ID: // https://www.figma.com/files/team//Team-Name -const TEAM_ID = '977136127691894995'; +const TEAM_ID = "977136127691894995" // Get this from the URL of a single file: // https://www.figma.com/file//Some-Name?node-id=182%3A0 -const FILE_KEY = 'FsQT67sVviqPXhYoTbpfIb'; +const FILE_KEY = "FsQT67sVviqPXhYoTbpfIb" -const fetch = require('node-fetch'); -const fs = require('fs'); -const { promisify } = require('util'); -const path = require('path'); +const fetch = require("node-fetch") +const fs = require("fs") +const { promisify } = require("util") +const path = require("path") -const writeFile = promisify(fs.writeFile); +const writeFile = promisify(fs.writeFile) -const personalToken = process.env.FIGMA_PERSONAL_TOKEN; +const personalToken = process.env.FIGMA_PERSONAL_TOKEN if (!personalToken) { - console.error('Please pass FIGMA_PERSONAL_TOKEN to this script and re-run'); - process.exit(1); + console.error("Please pass FIGMA_PERSONAL_TOKEN to this script and re-run") + process.exit(1) } -const figmaBase = 'https://api.figma.com/'; +const figmaBase = "https://api.figma.com/" const rgbToHex = (r, g, b) => { - const color = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); + const color = + "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) if (color.length > 7) { - return color.slice(0, 7); + return color.slice(0, 7) } - return color; -}; + return color +} -const slugify = (str) => str.toLowerCase().replace(/\s+/, '-'); +const slugify = (str) => str.toLowerCase().replace(/\s+/, "-") const doFetch = (url) => - fetch(`${figmaBase}v1${url}`, { - headers: { - 'X-Figma-Token': personalToken, - }, + fetch(`${figmaBase}v1${url}`, { + headers: { + "X-Figma-Token": personalToken, + }, + }) + .then((res) => { + if (!res.ok) { + throw new Error(`Status: ${res.status}`) + } + + return res.json() + }) + .then((json) => { + if (json.error || (json.status && json.status !== 200)) { + throw new Error(json.error || `Status ${json.status}: ${json.err}`) + } + + return json }) - .then((res) => { - if (!res.ok) { - throw new Error(`Status: ${res.status}`); - } - - return res.json(); - }) - .then((json) => { - if (json.error || (json.status && json.status !== 200)) { - throw new Error( - json.error || `Status ${json.status}: ${json.err}` - ); - } - - return json; - }); const fetchStyles = async (teamId) => { - const json = await doFetch(`/teams/${teamId}/styles?page_size=99`); + const json = await doFetch(`/teams/${teamId}/styles?page_size=99`) - return json.meta.styles; -}; + return json.meta.styles +} -const fetchFile = async (key) => await doFetch(`/files/${key}`); +const fetchFile = async (key) => await doFetch(`/files/${key}`) // eslint-disable-next-line -const fetchStyle = async (key) => await doFetch(`/styles/${key}`); +const fetchStyle = async (key) => await doFetch(`/styles/${key}`) // Retrieves all elements from groups from a given root const flattenTree = (root) => { - let ret = []; + let ret = [] - let { children = [] } = root; + let { children = [] } = root - for(let i = 0; i < children.length; i++) { - const el = children[i]; - const type = el.type; + for (let i = 0; i < children.length; i++) { + const el = children[i] + const type = el.type - if(type === "GROUP") { - let sub = flattenTree(el); - ret = ret.concat(sub); - } - else { + if (type === "GROUP") { + let sub = flattenTree(el) + ret = ret.concat(sub) + } else { ret.push(el) } } - return ret; + return ret } /** @@ -133,40 +131,39 @@ const flattenTree = (root) => { ``` */ const fetchAllColorStyles = async () => { - const styles = await fetchStyles(TEAM_ID); - const file = await fetchFile(FILE_KEY); + const styles = await fetchStyles(TEAM_ID) + const file = await fetchFile(FILE_KEY) - const root = file.document.children[0]; + const root = file.document.children[0] - if(!root) { + if (!root) { return [] } - const canvas = root.children.find((page) =>{ + const canvas = root.children.find((page) => { return page.id === PAGE_ID - }); + }) - const isRectOrVector = (c) => c.type === "RECTANGLE" || c.type === "VECTOR"; + const isRectOrVector = (c) => c.type === "RECTANGLE" || c.type === "VECTOR" console.log(JSON.stringify(canvas, null, 2)) - const allElements = flattenTree(canvas); + const allElements = flattenTree(canvas) - return ( - allElements - .filter((c) => isRectOrVector(c) && c.styles != null && c.styles.fill != null) + return allElements + .filter( + (c) => isRectOrVector(c) && c.styles != null && c.styles.fill != null, + ) .map((c) => { - const { r, g, b, a} = c.fills[0].color; - const { opacity = a } = c.fills[0]; - const nodeId = c.styles.fill; - - - let color; - if(opacity < 1) { - color = `rgba(${(r*255).toFixed(0)}, ${(g*255).toFixed(0)}, ${(b*255).toFixed(0)}, ${opacity.toFixed(2)})` - } - else { - color = rgbToHex(r * 256, g * 256, b * 256); + const { r, g, b, a } = c.fills[0].color + const { opacity = a } = c.fills[0] + const nodeId = c.styles.fill + + let color + if (opacity < 1) { + color = `rgba(${(r * 255).toFixed(0)}, ${(g * 255).toFixed(0)}, ${(b * 255).toFixed(0)}, ${opacity.toFixed(2)})` + } else { + color = rgbToHex(r * 256, g * 256, b * 256) } return { @@ -174,41 +171,40 @@ const fetchAllColorStyles = async () => { // give us the HEX color codes in their /styles endpoint .. :( ...styles.find((s) => s.node_id === nodeId), color, - }; + } }) .filter((c) => c.name != null) - ); -}; +} /** * Calls Figma's API and saves to a `colors.js` file in the project root. */ const writeColorsFromFigma = async () => { - const styles = await fetchAllColorStyles(); + const styles = await fetchAllColorStyles() - if (!styles) { - throw new Error('No styles found'); - } + if (!styles) { + throw new Error("No styles found") + } + + const colors = styles + .sort((a, b) => (a.sort_position < b.sort_position ? -1 : 1)) + .map((s) => { + return ( + (s.description ? ` /** ${s.description} */\n` : "") + + ` '${slugify(s.name)}': '${s.color}',` + ) + }) + .join("\n") - const colors = styles - .sort((a, b) => (a.sort_position < b.sort_position ? -1 : 1)) - .map( - (s) => { - return (s.description ? ` /** ${s.description} */\n` : '') + - ` '${slugify(s.name)}': '${s.color}',` - } - ) - .join('\n'); - - const fileContents = `/* eslint-disable */ + const fileContents = `/* eslint-disable */ /* Updated at ${new Date().toUTCString()}*/ module.exports = { ${colors} -}`; +}` - await writeFile(path.resolve(__dirname + '/../colors.js'), fileContents); + await writeFile(path.resolve(__dirname + "/../colors.js"), fileContents) - console.log(`Wrote ${styles.length} colors to colors.js`); -}; + console.log(`Wrote ${styles.length} colors to colors.js`) +} -writeColorsFromFigma().catch(console.error); +writeColorsFromFigma().catch(console.error) diff --git a/scripts/markdown.js b/scripts/markdown.js index 0d5584733..8ec5c34c6 100644 --- a/scripts/markdown.js +++ b/scripts/markdown.js @@ -1,72 +1,72 @@ -import { unified } from "unified"; -import remarkParse from "remark-parse"; -import remarkGfm from "remark-gfm"; -import remarkFrontmatter from "remark-frontmatter"; -import remarkRehype from "remark-rehype"; -import rehypeSlug from "rehype-slug"; -import rehypeStringify from "rehype-stringify"; -import { matter } from "vfile-matter"; +import { unified } from "unified" +import remarkParse from "remark-parse" +import remarkGfm from "remark-gfm" +import remarkFrontmatter from "remark-frontmatter" +import remarkRehype from "remark-rehype" +import rehypeSlug from "rehype-slug" +import rehypeStringify from "rehype-stringify" +import { matter } from "vfile-matter" -const remarkVfileMatter = options => (tree, file) => { - matter(file); -}; +const remarkVfileMatter = (options) => (tree, file) => { + matter(file) +} -const remarkCodeblocks = options => (tree, file) => { - const { children } = tree; - const codeblocks = {}; +const remarkCodeblocks = (options) => (tree, file) => { + const { children } = tree + const codeblocks = {} - const formatter = value => { + const formatter = (value) => { // Strip newlines and weird spacing return value .replace(/\n/g, " ") .replace(/\s+/g, " ") .replace(/\(\s+/g, "(") - .replace(/\s+\)/g, ")"); - }; + .replace(/\s+\)/g, ")") + } - children.forEach(child => { + children.forEach((child) => { if (child.type === "code" && child.value) { - const { meta, lang } = child; + const { meta, lang } = child if (meta === "sig" && lang === "re") { if (codeblocks[lang] == null) { - codeblocks[lang] = []; + codeblocks[lang] = [] } - codeblocks[lang].push(formatter(child.value)); + codeblocks[lang].push(formatter(child.value)) } } - }); + }) - Object.assign(file.data, { codeblocks }); -}; + Object.assign(file.data, { codeblocks }) +} -const rehypeHeaders = options => (tree, file) => { - const headers = []; - let mainHeader; - tree.children.forEach(child => { +const rehypeHeaders = (options) => (tree, file) => { + const headers = [] + let mainHeader + tree.children.forEach((child) => { if (child.tagName === "h1") { if (child.children.length > 0) { - mainHeader = child.children.map(element => element.value).join(""); + mainHeader = child.children.map((element) => element.value).join("") } } if (child.tagName === "h2") { if (child.children.length > 0) { - const id = child.properties.id || ""; - const name = child.children.map(element => element.value).join(""); - headers.push({ name, href: id }); + const id = child.properties.id || "" + const name = child.children.map((element) => element.value).join("") + headers.push({ name, href: id }) } } - }); + }) - Object.assign(file.data, { headers, mainHeader }); -}; + Object.assign(file.data, { headers, mainHeader }) +} export const defaultProcessor = unified() .use(remarkParse) .use(remarkGfm) - .use(remarkFrontmatter, [{ type: 'yaml', marker: '-' }]) + .use(remarkFrontmatter, [{ type: "yaml", marker: "-" }]) .use(remarkVfileMatter) .use(remarkCodeblocks) .use(remarkRehype) .use(rehypeSlug) .use(rehypeHeaders) - .use(rehypeStringify); + .use(rehypeStringify) diff --git a/scripts/sync-playground-bundles.mjs b/scripts/sync-playground-bundles.mjs index da9850862..dc414d57c 100644 --- a/scripts/sync-playground-bundles.mjs +++ b/scripts/sync-playground-bundles.mjs @@ -1,45 +1,51 @@ -import * as path from "node:path"; -import * as fs from "node:fs"; -import * as childProcess from "node:child_process"; -import { Readable } from "node:stream"; -import * as stream from "node:stream/promises"; +import * as path from "node:path" +import * as fs from "node:fs" +import * as childProcess from "node:child_process" +import { Readable } from "node:stream" +import * as stream from "node:stream/promises" -const bucketUrl = new URL("https://cdn.rescript-lang.org"); +const bucketUrl = new URL("https://cdn.rescript-lang.org") -const bundlesDir = path.join(import.meta.dirname, "../public/playground-bundles"); -fs.mkdirSync(bundlesDir, { recursive: true }); +const bundlesDir = path.join( + import.meta.dirname, + "../public/playground-bundles", +) +fs.mkdirSync(bundlesDir, { recursive: true }) -const versions = await fetch(new URL("/playground-bundles/versions.json", bucketUrl)) - .then(res => res.json()); +const versions = await fetch( + new URL("/playground-bundles/versions.json", bucketUrl), +).then((res) => res.json()) for (const version of versions) { - const versionDir = path.join(bundlesDir, version); - const compilerFile = path.join(versionDir, "compiler.js"); + const versionDir = path.join(bundlesDir, version) + const compilerFile = path.join(versionDir, "compiler.js") if (fs.existsSync(compilerFile)) { - console.log(`%s has already been synced.`, version); - continue; + console.log(`%s has already been synced.`, version) + continue } - console.group(`Syncing %s...`, version); + console.group(`Syncing %s...`, version) { - console.log(`Downloading archive file...`); - const res = await fetch(new URL(`/playground-bundles/${version}.tar.zst`, bucketUrl)); + console.log(`Downloading archive file...`) + const res = await fetch( + new URL(`/playground-bundles/${version}.tar.zst`, bucketUrl), + ) if (!res.ok) { - console.error(await res.text()); - continue; + console.error(await res.text()) + continue } - const archiveFile = path.join(bundlesDir, `${version}.tar.zst`); - const fileStream = fs.createWriteStream(archiveFile); - await stream.finished(Readable.fromWeb(res.body).pipe(fileStream)); + const archiveFile = path.join(bundlesDir, `${version}.tar.zst`) + const fileStream = fs.createWriteStream(archiveFile) + await stream.finished(Readable.fromWeb(res.body).pipe(fileStream)) - console.log("Extracting archive..."); - fs.mkdirSync(versionDir, { recursive: true }); - childProcess.execSync(`tar --zstd -xf "${archiveFile}" -C "${versionDir}"`); + console.log("Extracting archive...") + fs.mkdirSync(versionDir, { recursive: true }) + childProcess.execSync(`tar --zstd -xf "${archiveFile}" -C "${versionDir}"`) - console.log("Cleaning up..."); - fs.unlinkSync(archiveFile); + console.log("Cleaning up...") + fs.unlinkSync(archiveFile) - console.groupEnd(); + console.groupEnd() } } diff --git a/scripts/sync-redirects.mjs b/scripts/sync-redirects.mjs index 566d842bc..d7434d4f6 100644 --- a/scripts/sync-redirects.mjs +++ b/scripts/sync-redirects.mjs @@ -1,25 +1,21 @@ -import * as path from "node:path"; -import * as fs from "node:fs"; +import * as path from "node:path" +import * as fs from "node:fs" -import nextConfig from "../next.config.mjs"; +import nextConfig from "../next.config.mjs" -const redirectsConfig = await nextConfig.redirects(); +const redirectsConfig = await nextConfig.redirects() /** * @param {{ * source: string, * destination: string, * permanent: boolean, - * }} config + * }} config * @return {string} */ -function lineFormat({ - source, - destination, - permanent, -}) { - return `${source} ${destination} ${permanent ? 308 : 307}`; +function lineFormat({ source, destination, permanent }) { + return `${source} ${destination} ${permanent ? 308 : 307}` } -const redirects = redirectsConfig.map(lineFormat).join("\n"); -const redirectsFile = path.join(import.meta.dirname, "../public/_redirects"); +const redirects = redirectsConfig.map(lineFormat).join("\n") +const redirectsFile = path.join(import.meta.dirname, "../public/_redirects") diff --git a/scripts/test-examples.mjs b/scripts/test-examples.mjs index f7acca969..9ff62b22b 100644 --- a/scripts/test-examples.mjs +++ b/scripts/test-examples.mjs @@ -1,124 +1,168 @@ -import glob from "glob"; -import fs from "fs"; -import child_process from "child_process"; -import path from "path"; -import { URL } from 'url'; +import glob from "glob" +import fs from "fs" +import child_process from "child_process" +import path from "path" +import { URL } from "url" -const pathname = new URL('.', import.meta.url).pathname; -const __dirname = process.platform !== 'win32' ? pathname : pathname.substring(1) +const pathname = new URL(".", import.meta.url).pathname +const __dirname = + process.platform !== "win32" ? pathname : pathname.substring(1) -let tempFileName = path.join(__dirname, '..', '_tempFile.res') +let tempFileName = path.join(__dirname, "..", "_tempFile.res") let tempFileNameRegex = /_tempFile\.res/g // TODO: In the future we need to use the appropriate rescript version for each doc version variant // see the package.json on how to define another rescript version let compilersDir = path.join(__dirname, "..", "compilers") -let bsc = path.join(compilersDir, 'node_modules', 'rescript-1110', process.platform, 'bsc.exe'); -let rescriptBin = path.join(compilersDir, 'node_modules', 'rescript-1110', 'rescript'); -let rescriptCoreCompiled = path.join(compilersDir, 'node_modules', '@rescript', 'core', 'lib', 'ocaml'); -let rescriptReactCompiled = path.join(compilersDir, 'node_modules', '@rescript', 'react', 'lib', 'ocaml'); +let bsc = path.join( + compilersDir, + "node_modules", + "rescript-1110", + process.platform, + "bsc.exe", +) +let rescriptBin = path.join( + compilersDir, + "node_modules", + "rescript-1110", + "rescript", +) +let rescriptCoreCompiled = path.join( + compilersDir, + "node_modules", + "@rescript", + "core", + "lib", + "ocaml", +) +let rescriptReactCompiled = path.join( + compilersDir, + "node_modules", + "@rescript", + "react", + "lib", + "ocaml", +) const prepareCompilers = () => { if (fs.existsSync(bsc)) { - return; + return } - console.log("compilers not installed. Installing compilers..."); - child_process.execFileSync("npm", ['install'], {cwd: compilersDir}) + console.log("compilers not installed. Installing compilers...") + child_process.execFileSync("npm", ["install"], { cwd: compilersDir }) } const prepareDependencies = () => { - if (fs.existsSync(rescriptCoreCompiled) && fs.existsSync(rescriptReactCompiled)) { - return; + if ( + fs.existsSync(rescriptCoreCompiled) && + fs.existsSync(rescriptReactCompiled) + ) { + return } - console.log("Dependencies not installed. Installing..."); - child_process.execFileSync(rescriptBin, [], {cwd: compilersDir}) + console.log("Dependencies not installed. Installing...") + child_process.execFileSync(rescriptBin, [], { cwd: compilersDir }) } -let parseFile = content => { +let parseFile = (content) => { if (!/```res (example|prelude|sig)/.test(content)) { return } let inCodeBlock = false let moduleId = 0 - return content.split('\n').map(line => { - let modifiedLine = '' - if (line.startsWith('```res example')) { - inCodeBlock = true - modifiedLine = `/* _MODULE_EXAMPLE_START */ module M_${moduleId++} = {` - } else if (line.startsWith('```res prelude')) { - inCodeBlock = true - modifiedLine = `/* _MODULE_PRELUDE_START */ include {` - } else if (line.startsWith('```res sig')) { - inCodeBlock = true - modifiedLine = `/* _MODULE_SIG_START */ module type M_${moduleId++} = {` - } else if (inCodeBlock) { - if (line.startsWith('```')) { - inCodeBlock = false - modifiedLine = '} // _MODULE_END' - } else { - modifiedLine = line + return content + .split("\n") + .map((line) => { + let modifiedLine = "" + if (line.startsWith("```res example")) { + inCodeBlock = true + modifiedLine = `/* _MODULE_EXAMPLE_START */ module M_${moduleId++} = {` + } else if (line.startsWith("```res prelude")) { + inCodeBlock = true + modifiedLine = `/* _MODULE_PRELUDE_START */ include {` + } else if (line.startsWith("```res sig")) { + inCodeBlock = true + modifiedLine = `/* _MODULE_SIG_START */ module type M_${moduleId++} = {` + } else if (inCodeBlock) { + if (line.startsWith("```")) { + inCodeBlock = false + modifiedLine = "} // _MODULE_END" + } else { + modifiedLine = line + } } - } - return modifiedLine - }).join('\n') + return modifiedLine + }) + .join("\n") } let postprocessOutput = (file, error) => { - return error.stderr.toString() - .replace(tempFileNameRegex, path.relative('.', file)) - .replace(/\/\* _MODULE_(EXAMPLE|PRELUDE|SIG)_START \*\/.+/g, (_, capture) => { - return '```res ' + (capture === 'EXAMPLE' ? 'example' : capture === 'PRELUDE' ? 'prelude' : 'sig') - }) + return error.stderr + .toString() + .replace(tempFileNameRegex, path.relative(".", file)) + .replace( + /\/\* _MODULE_(EXAMPLE|PRELUDE|SIG)_START \*\/.+/g, + (_, capture) => { + return ( + "```res " + + (capture === "EXAMPLE" + ? "example" + : capture === "PRELUDE" + ? "prelude" + : "sig") + ) + }, + ) .replace(/(.*)\}(.*)\/\/ _MODULE_END/g, (_, cap1, cap2) => { // cap1 cap2 might be empty or ansi coloring code - return cap1 + '```' + cap2 + return cap1 + "```" + cap2 }) } - -prepareCompilers(); -prepareDependencies(); +prepareCompilers() +prepareDependencies() console.log("Running tests...") -fs.writeFileSync(tempFileName, '') +fs.writeFileSync(tempFileName, "") let success = true -glob.sync(__dirname + '/../pages/docs/{manual/latest,react/latest}/**/*.mdx').forEach((file) => { - let content = fs.readFileSync(file, {encoding: 'utf-8'}) - let parsedResult = parseFile(content) - if (parsedResult != null) { - fs.writeFileSync(tempFileName, parsedResult) - try { - // -109 for suppressing `Toplevel expression is expected to have unit type.` - // Most doc snippets do e.g. `Belt.Array.length(["test"])`, which triggers this - child_process.execFileSync( - bsc, - [ - tempFileName, - '-I', - rescriptCoreCompiled, - '-I', - rescriptReactCompiled, - '-bs-jsx', - '4', - '-w', - '-109', - '-uncurried', - '-open', - 'RescriptCore', - ], - { stdio: "pipe" } - ); - } catch (e) { - process.stdout.write(postprocessOutput(file, e)) - success = false +glob + .sync(__dirname + "/../pages/docs/{manual/latest,react/latest}/**/*.mdx") + .forEach((file) => { + let content = fs.readFileSync(file, { encoding: "utf-8" }) + let parsedResult = parseFile(content) + if (parsedResult != null) { + fs.writeFileSync(tempFileName, parsedResult) + try { + // -109 for suppressing `Toplevel expression is expected to have unit type.` + // Most doc snippets do e.g. `Belt.Array.length(["test"])`, which triggers this + child_process.execFileSync( + bsc, + [ + tempFileName, + "-I", + rescriptCoreCompiled, + "-I", + rescriptReactCompiled, + "-bs-jsx", + "4", + "-w", + "-109", + "-uncurried", + "-open", + "RescriptCore", + ], + { stdio: "pipe" }, + ) + } catch (e) { + process.stdout.write(postprocessOutput(file, e)) + success = false + } } - } -}) + }) fs.unlinkSync(tempFileName) process.exit(success ? 0 : 1) diff --git a/scripts/test-hrefs.mjs b/scripts/test-hrefs.mjs index 11c744cc8..e929d44ba 100644 --- a/scripts/test-hrefs.mjs +++ b/scripts/test-hrefs.mjs @@ -7,152 +7,156 @@ */ import { config } from "dotenv" -import glob from "glob"; -import path from "path"; -import fs from "fs"; -import urlModule from "url"; -import { URL } from 'url'; -import { getAllPosts, blogPathToSlug } from '../src/common/BlogApi.mjs' +import glob from "glob" +import path from "path" +import fs from "fs" +import urlModule from "url" +import { URL } from "url" +import { getAllPosts, blogPathToSlug } from "../src/common/BlogApi.mjs" -import { defaultProcessor } from "./markdown.js"; +import { defaultProcessor } from "./markdown.js" config() let latestVersion = process.env.VERSION_LATEST let nextVersion = process.env.VERSION_NEXT -const pathname = new URL('.', import.meta.url).pathname; -const __dirname = process.platform !== 'win32' ? pathname : pathname.substring(1) +const pathname = new URL(".", import.meta.url).pathname +const __dirname = + process.platform !== "win32" ? pathname : pathname.substring(1) -const mapBlogFilePath = path => { - const match = path.match(/\.\/_blogposts\/(.*\.mdx)/); +const mapBlogFilePath = (path) => { + const match = path.match(/\.\/_blogposts\/(.*\.mdx)/) if (match) { - let relPath = match[1]; - let data = getAllPosts().find(({ path }) => path === relPath); + let relPath = match[1] + let data = getAllPosts().find(({ path }) => path === relPath) if (data != null) { - return `./pages/blog/${blogPathToSlug(data.path)}`; + return `./pages/blog/${blogPathToSlug(data.path)}` } - return path; + return path } - return path; -}; + return path +} // Static files are located in /public/static/img/somefile.png // within markdown files they are referenced as /static/img/somefile.png -const mapStaticFilePath = path => { - return path.replace("./public", ""); +const mapStaticFilePath = (path) => { + return path.replace("./public", "") } // Creates a lookup table of all available pages within the website // It will also automatically map urls for dedicated directorys (such as _blogposts) // to the correct url // { key=url: value=original_filepath} -const createPageIndex = files => { +const createPageIndex = (files) => { return files.reduce((acc, path) => { // We need to consider all the different file formats used in pages // Calculate the website url by stripping .re, .bs.js, .md(x), etc. - let url; + let url if (path.startsWith("./_blogposts")) { url = mapBlogFilePath(path) - } - else if (path.startsWith("./public/static")) { - url = mapStaticFilePath(path); - } - else { - url = path; + } else if (path.startsWith("./public/static")) { + url = mapStaticFilePath(path) + } else { + url = path } - url = url.replace(/^\.\//, "/").replace(/\.re|\.bs\.js|\.js|\.md(x)?$/, ""); + url = url.replace(/^\.\//, "/").replace(/\.re|\.bs\.js|\.js|\.md(x)?$/, "") // For index we need to special case, since it can be referred as '/' as well if (path.match(/\.\/pages\/index(\.re|\.bs\.js|\.js|\.md(x))?$/)) { - url = "/pages/"; + url = "/pages/" } - acc[url] = path; - return acc; - }, {}); -}; + acc[url] = path + return acc + }, {}) +} -const flattenChildren = children => { +const flattenChildren = (children) => { return children.reduce((acc, node) => { if (node.type === "link") { - return acc.concat([node]); + return acc.concat([node]) } else if (node.children) { - let value = flattenChildren(node.children); - return acc.concat(value); + let value = flattenChildren(node.children) + return acc.concat(value) } - return acc; - }, []); -}; + return acc + }, []) +} // Finds all relative links within a file -const hrefs = options => (tree, file) => { - const links = flattenChildren(tree.children); +const hrefs = (options) => (tree, file) => { + const links = flattenChildren(tree.children) - file.data = Object.assign({}, file.data, { links }); -}; + file.data = Object.assign({}, file.data, { links }) +} -const processor = defaultProcessor.use(hrefs); +const processor = defaultProcessor.use(hrefs) -const processFile = filepath => { - const content = fs.readFileSync(filepath, "utf8"); - const result = processor.processSync(content); +const processFile = (filepath) => { + const content = fs.readFileSync(filepath, "utf8") + const result = processor.processSync(content) - result.data.filepath = filepath; + result.data.filepath = filepath - return result.data; -}; + return result.data +} -const showErrorMsg = failedTest => { - const { stderr } = failedTest; - console.log(`\n-----------\nError Preview:`); - console.log(stderr); -}; +const showErrorMsg = (failedTest) => { + const { stderr } = failedTest + console.log(`\n-----------\nError Preview:`) + console.log(stderr) +} -const createApiIndexModules = version => { - const dir = path.join(__dirname, "..", "data", "api", version); - const modules = fs.readdirSync(dir).filter(file => file !== "toc_tree.json"); +const createApiIndexModules = (version) => { + const dir = path.join(__dirname, "..", "data", "api", version) + const modules = fs.readdirSync(dir).filter((file) => file !== "toc_tree.json") const paths = modules.reduce((acc, file) => { - const json = JSON.parse(fs.readFileSync(path.join(dir, file))); - const keys = Object.keys(json); + const json = JSON.parse(fs.readFileSync(path.join(dir, file))) + const keys = Object.keys(json) - const paths = keys.map(modulePath => path.join(version, "api", modulePath)); + const paths = keys.map((modulePath) => + path.join(version, "api", modulePath), + ) - return acc.concat(paths); - }, []); - return [`${version}/api`, ...paths]; -}; + return acc.concat(paths) + }, []) + return [`${version}/api`, ...paths] +} -const apiIndexModules = [...createApiIndexModules(latestVersion), ...createApiIndexModules(nextVersion)] +const apiIndexModules = [ + ...createApiIndexModules(latestVersion), + ...createApiIndexModules(nextVersion), +] const testFile = (pageMap, test) => { - const filepath = test.filepath; + const filepath = test.filepath // Used for storing failed / ok hrefs - const results = []; + const results = [] - test.links.forEach(link => { + test.links.forEach((link) => { // Simulate the redirect of "latest" and "next" version aliases. if (link.url.includes("/manual/latest/")) { - link.url = link.url.replace("/latest/", `/${latestVersion}/`); + link.url = link.url.replace("/latest/", `/${latestVersion}/`) } if (link.url.includes("/manual/next/")) { - link.url = link.url.replace("/next/", `/${nextVersion}/`); + link.url = link.url.replace("/next/", `/${nextVersion}/`) } - const parsed = urlModule.parse(link.url); + const parsed = urlModule.parse(link.url) // Drops .md / .mdx / .html file extension in pathname section, since UI ignores them // Needs to be kept in sync with `components/Markdown.re`s component // This requirements stems from the original documentation on reasonml.github.io, where lots of .md / .html // hrefs are included - let url = link.url; + let url = link.url if (parsed.pathname) { - parsed.pathname = parsed.pathname.replace(/\.md(x)?|\.html$/, ""); - url = urlModule.format(parsed); + parsed.pathname = parsed.pathname.replace(/\.md(x)?|\.html$/, "") + url = urlModule.format(parsed) } // Scenarios where links should NOT be checked @@ -173,134 +177,130 @@ const testFile = (pageMap, test) => { ) { // If there is a relative link like '../manual/latest', we need to resolve it // relatively from the links source filepath - let resolved; + let resolved if (!path.isAbsolute(url)) { - resolved = path.join("/", path.dirname(filepath), parsed.pathname); - } - else { + resolved = path.join("/", path.dirname(filepath), parsed.pathname) + } else { if (parsed.pathname.startsWith("/static")) { - console.log("Static"); - resolved = path.join(parsed.pathname); - } - else { + console.log("Static") + resolved = path.join(parsed.pathname) + } else { // e.g. /api/javascript/latest/js needs to be prefixed to actual pages dir - resolved = path.join("/pages", parsed.pathname); + resolved = path.join("/pages", parsed.pathname) } } - - if ( resolved.startsWith(`/pages/docs/manual/${latestVersion}/api`) || resolved.startsWith(`/pages/docs/manual/${nextVersion}/api`) ) { - const pathToModule = resolved.replace("/pages/docs/manual/", ""); - const pathExists = apiIndexModules.includes(pathToModule); + const pathToModule = resolved.replace("/pages/docs/manual/", "") + const pathExists = apiIndexModules.includes(pathToModule) if (pathExists) { results.push({ status: "ok", - link - }); + link, + }) } else { - const { line, column } = link.position.start; - const stderr = `${filepath}: Unknown href '${url}' in line ${line}:${column}`; + const { line, column } = link.position.start + const stderr = `${filepath}: Unknown href '${url}' in line ${line}:${column}` results.push({ status: "failed", filepath, stderr, - link - }); + link, + }) } - return; + return } // If there's no page stated the relative link if (!pageMap[resolved]) { - const { line, column } = link.position.start; - const stderr = `${filepath}: Unknown href '${url}' in line ${line}:${column}`; + const { line, column } = link.position.start + const stderr = `${filepath}: Unknown href '${url}' in line ${line}:${column}` results.push({ status: "failed", filepath, stderr, - link - }); - return; + link, + }) + return } } results.push({ status: "ok", - link - }); - }); + link, + }) + }) if (results.length > 0) { - console.log(`\n-------Results for '${filepath}'----------`); + console.log(`\n-------Results for '${filepath}'----------`) - results.forEach(r => { - const { status } = r; - const { line, column } = r.link.position.start; + results.forEach((r) => { + const { status } = r + const { line, column } = r.link.position.start if (status === "failed") { console.log( - `${filepath}:${line} => ${status} / Unknown href '${r.link.url}' in line ${line}:${column}` - ); + `${filepath}:${line} => ${status} / Unknown href '${r.link.url}' in line ${line}:${column}`, + ) } else { - console.log(`${filepath}:${line} => ${status}`); + console.log(`${filepath}:${line} => ${status}`) } - }); + }) } return { data: test, - results - }; -}; + results, + } +} const main = () => { - const [, , pattern] = process.argv; - const cwd = path.join(__dirname, ".."); + const [, , pattern] = process.argv + const cwd = path.join(__dirname, "..") // All files that are going to be tested for broken links const files = glob.sync( pattern ? pattern : `./{pages,_blogposts,misc_docs}/**/*.md?(x)`, - { cwd } - ); + { cwd }, + ) // We need to capture all files independently from the test file glob - const pageMapFiles = glob.sync("./{pages,_blogposts}/**/*.{js,mdx}", { cwd }); - const staticFiles = glob.sync("./public/static/**/*.{svg,png,woff2}", { cwd }); + const pageMapFiles = glob.sync("./{pages,_blogposts}/**/*.{js,mdx}", { cwd }) + const staticFiles = glob.sync("./public/static/**/*.{svg,png,woff2}", { cwd }) - const allFiles = pageMapFiles.concat(staticFiles); + const allFiles = pageMapFiles.concat(staticFiles) - const pageMap = createPageIndex(allFiles); + const pageMap = createPageIndex(allFiles) - const processedFiles = files.map(processFile); + const processedFiles = files.map(processFile) - const allTested = processedFiles.map(file => testFile(pageMap, file)); + const allTested = processedFiles.map((file) => testFile(pageMap, file)) const failed = allTested.reduce((acc, test) => { - return acc.concat(test.results.filter(r => r.status === "failed")); - }, []); + return acc.concat(test.results.filter((r) => r.status === "failed")) + }, []) const success = allTested.reduce((acc, test) => { - return acc.concat(test.results.filter(r => r.status === "ok")); - }, []); + return acc.concat(test.results.filter((r) => r.status === "ok")) + }, []) - console.log("-----------\nSummary:"); - console.log(`Total Links: ${failed.length + success.length}`); - console.log(`Failed: ${failed.length}`); - console.log(`Success: ${success.length}`); + console.log("-----------\nSummary:") + console.log(`Total Links: ${failed.length + success.length}`) + console.log(`Failed: ${failed.length}`) + console.log(`Success: ${success.length}`) if (failed.length > 0) { console.log( - `\nTip: You can also run tests just for specific files / globs:` - ); - console.log('`node scripts/test-hrefs.js "pages/**/*.mdx"`'); - showErrorMsg(failed[0]); - process.exit(1); + `\nTip: You can also run tests just for specific files / globs:`, + ) + console.log('`node scripts/test-hrefs.js "pages/**/*.mdx"`') + showErrorMsg(failed[0]) + process.exit(1) } -}; +} -main(); +main() diff --git a/src/ffi/loadScript.js b/src/ffi/loadScript.js index fe79fa3ea..fe8bf51d6 100644 --- a/src/ffi/loadScript.js +++ b/src/ffi/loadScript.js @@ -1,26 +1,26 @@ const isBrowser = - typeof window !== 'undefined' && typeof window.document !== 'undefined'; + typeof window !== "undefined" && typeof window.document !== "undefined" export default function loadScript(src, onSuccess, onError) { - if (!isBrowser) return; + if (!isBrowser) return - const scriptEl = document.createElement("script"); - scriptEl.setAttribute("src", src); + const scriptEl = document.createElement("script") + scriptEl.setAttribute("src", src) - scriptEl.addEventListener("load", onSuccess); - scriptEl.addEventListener("error", onError); + scriptEl.addEventListener("load", onSuccess) + scriptEl.addEventListener("error", onError) - document.body.appendChild(scriptEl); + document.body.appendChild(scriptEl) return () => { - scriptEl.removeEventListener("load", onSuccess); - scriptEl.removeEventListener("error", onError); - }; + scriptEl.removeEventListener("load", onSuccess) + scriptEl.removeEventListener("error", onError) + } } export function removeScript(src) { - const existing = document.body.querySelectorAll(`script[src="${src}"]`); + const existing = document.body.querySelectorAll(`script[src="${src}"]`) existing.forEach((el) => { - document.body.removeChild(el); + document.body.removeChild(el) }) } diff --git a/src/ffi/parse-numeric-range.js b/src/ffi/parse-numeric-range.js index 2b9f8986d..15e098658 100644 --- a/src/ffi/parse-numeric-range.js +++ b/src/ffi/parse-numeric-range.js @@ -5,32 +5,32 @@ * @returns {Array} Returns an energetic array. */ export function parsePart(string) { - console.log("string", string); - let res = []; - let m; + console.log("string", string) + let res = [] + let m for (let str of string.split(",").map((str) => str.trim())) { // just a number if (/^-?\d+$/.test(str)) { - res.push(parseInt(str, 10)); + res.push(parseInt(str, 10)) } else if ( (m = str.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)) ) { // 1-5 or 1..5 (equivalent) or 1...5 (doesn't include 5) - let [_, lhs, sep, rhs] = m; + let [_, lhs, sep, rhs] = m if (lhs && rhs) { - lhs = parseInt(lhs); - rhs = parseInt(rhs); - const incr = lhs < rhs ? 1 : -1; + lhs = parseInt(lhs) + rhs = parseInt(rhs) + const incr = lhs < rhs ? 1 : -1 // Make it inclusive by moving the right 'stop-point' away by one. - if (sep === "-" || sep === ".." || sep === "\u2025") rhs += incr; + if (sep === "-" || sep === ".." || sep === "\u2025") rhs += incr - for (let i = lhs; i !== rhs; i += incr) res.push(i); + for (let i = lhs; i !== rhs; i += incr) res.push(i) } } } - return res; + return res } diff --git a/src/ffi/react-codemirror-hooks.js b/src/ffi/react-codemirror-hooks.js index 0821f0edf..29bb67b87 100644 --- a/src/ffi/react-codemirror-hooks.js +++ b/src/ffi/react-codemirror-hooks.js @@ -2,162 +2,162 @@ // OBSOLETE! // // This file is reimplemented in common/CodeMirrorBase.re -// We keep this around for reference in case we find some +// We keep this around for reference in case we find some // bugs in the new implementation // -import React, { useEffect, useRef } from "react"; -import CodeMirror from "codemirror"; -import "codemirror/lib/codemirror.css"; -import "../styles/cm.css"; +import React, { useEffect, useRef } from "react" +import CodeMirror from "codemirror" +import "codemirror/lib/codemirror.css" +import "../styles/cm.css" if (typeof window !== "undefined" && typeof window.navigator !== "undefined") { - require("codemirror/mode/javascript/javascript"); - require("../plugins/cm-reason-mode"); + require("codemirror/mode/javascript/javascript") + require("../plugins/cm-reason-mode") } function makeErrorMarker(cm) { - const wrapper = cm.getWrapperElement(); + const wrapper = cm.getWrapperElement() - const msg = document.createElement("div"); - msg.className = "z-10 absolute px-2 rounded bg-fire-15"; - msg.innerHTML = "Some Error Message"; - msg.style.display = "none"; + const msg = document.createElement("div") + msg.className = "z-10 absolute px-2 rounded bg-fire-15" + msg.innerHTML = "Some Error Message" + msg.style.display = "none" const showMsg = () => { - msg.style.display = "block"; - }; + msg.style.display = "block" + } const hideMsg = () => { - msg.style.display = "none"; - }; + msg.style.display = "none" + } - const marker = document.createElement("div"); + const marker = document.createElement("div") marker.className = - "text-center text-fire font-bold bg-fire-15 rounded w-4 ml-2 hover:cursor-pointer"; - marker.innerHTML = "!"; + "text-center text-fire font-bold bg-fire-15 rounded w-4 ml-2 hover:cursor-pointer" + marker.innerHTML = "!" marker.onmouseover = function markerMouseOver() { - const wrapperPos = wrapper.getBoundingClientRect(); - const pos = marker.getBoundingClientRect(); + const wrapperPos = wrapper.getBoundingClientRect() + const pos = marker.getBoundingClientRect() - msg.style.left = pos.x - wrapperPos.x + pos.width + "px"; - msg.style.top = pos.y - wrapperPos.y + "px"; + msg.style.left = pos.x - wrapperPos.x + pos.width + "px" + msg.style.top = pos.y - wrapperPos.y + "px" - console.log("wrapperPos", wrapperPos); - console.log("markerPos", pos); - console.log(msg.style.left); + console.log("wrapperPos", wrapperPos) + console.log("markerPos", pos) + console.log(msg.style.left) - showMsg(); - }; + showMsg() + } marker.onmouseleave = function markerMouseLeave() { - hideMsg(); - }; + hideMsg() + } - wrapper.appendChild(msg); + wrapper.appendChild(msg) - return marker; + return marker } -const ERROR_GUTTER_ID = "errors"; +const ERROR_GUTTER_ID = "errors" function clearMarks(cm) { - var state = cm.state.errors; + var state = cm.state.errors for (var i = 0; i < state.marked.length; ++i) { - state.marked[i].clear(); + state.marked[i].clear() } - state.marked.length = 0; + state.marked.length = 0 } function updateErrors(cm, errors) { // Clear out all previous errors first - clearMarks(cm); - cm.clearGutter(ERROR_GUTTER_ID); + clearMarks(cm) + cm.clearGutter(ERROR_GUTTER_ID) - const state = cm.state.errors; + const state = cm.state.errors errors.forEach((e) => { - const marker = makeErrorMarker(cm); - cm.setGutterMarker(e.row, ERROR_GUTTER_ID, marker); + const marker = makeErrorMarker(cm) + cm.setGutterMarker(e.row, ERROR_GUTTER_ID, marker) + + const from = { line: e.row, ch: e.column } + const to = { line: e.endRow, ch: e.endColumn } - const from = {line: e.row, ch: e.column}; - const to = {line: e.endRow, ch: e.endColumn}; - - state.marked.push(cm.markText(from, to, {className: "bg-fire-15"})); - console.log("marked", state.marked); - }); + state.marked.push(cm.markText(from, to, { className: "bg-fire-15" })) + console.log("marked", state.marked) + }) //cm.setGutterMarker(0, ERROR_GUTTER_ID, marker); } export default function CodeMirrorReact(props) { - const options = props.options || {}; + const options = props.options || {} - const errors = props.errors || []; - const value = props.value || ""; + const errors = props.errors || [] + const value = props.value || "" - const inputElement = useRef(); - const cmRef = useRef(); + const inputElement = useRef() + const cmRef = useRef() useEffect(() => { const cm = CodeMirror.fromTextArea(inputElement.current, { theme: "material", gutters: [ERROR_GUTTER_ID, "CodeMirror-linenumbers"], - ...options - }); + ...options, + }) - cm.getScrollerElement().style.minHeight = props.minHeight; - cm.refresh(); + cm.getScrollerElement().style.minHeight = props.minHeight + cm.refresh() // For some reason, injecting value with the options doesn't work // so we need to set the initial value imperatively - cm.setValue(value); + cm.setValue(value) // Set up event handlers - const onChange = props.onChange; + const onChange = props.onChange if (onChange != null) { cm.on("change", (instance, obj) => { - onChange(instance.getValue()); - }); + onChange(instance.getValue()) + }) } - cmRef.current = cm; + cmRef.current = cm // Set state - cm.state.errors = { marked: [] }; + cm.state.errors = { marked: [] } return () => { - console.log("cleanup", options.mode); + console.log("cleanup", options.mode) // This will destroy the CM instance - cm.toTextArea(); - }; - }, []); + cm.toTextArea() + } + }, []) useEffect(() => { - const cm = cmRef.current; + const cm = cmRef.current if (cm != null) { - cm.setValue(value); + cm.setValue(value) } - }, [value]); + }, [value]) useEffect(() => { - const cm = cmRef.current; + const cm = cmRef.current if (cm != null) { //console.log("setting errors"); //console.log(errors); cm.operation(() => { - updateErrors(cm, errors); - }); + updateErrors(cm, errors) + }) } - }, [errors]); + }, [errors]) return (