diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7c68b07 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# https://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..240b092 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "env": { "browser": true }, + "extends": "eslint:recommended", + "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, + "rules": { + "block-scoped-var": "error", + "consistent-return": "error", + "eqeqeq": "error", + "guard-for-in": "error", + "no-bitwise": "error", + "no-caller": "error", + "no-extra-parens": "off", + "no-extend-native": "error", + "no-loop-func": "error", + "no-new": "error", + "no-param-reassign": "error", + "no-return-assign": "error", + "no-sequences": "error", + "no-unused-expressions": "error", + "no-use-before-define": "error", + "no-undef": "error", + "no-eq-null": "error", + "radix": ["error", "always"], + "indent": ["error", 2, { "SwitchCase": 1 }], + "quotes": ["error", "double"], + "semi": ["error", "always"], + "strict": ["error", "global"] + } +} diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..b79b995 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,19 @@ +--- +name: Bug report +about: Report an issue +title: "" +labels: bug +assignees: "" +--- + +**Describe the bug** +A clear and concise description of the bug. + +**To Reproduce** +A minimal code example (preferably a runnable example on [Try PureScript](https://try.purescript.org)!) or steps to reproduce the issue. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/change-request.md b/.github/ISSUE_TEMPLATE/change-request.md new file mode 100644 index 0000000..a2ee685 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/change-request.md @@ -0,0 +1,21 @@ +--- +name: Change request +about: Propose an improvement to this library +title: "" +labels: "" +assignees: "" +--- + +**Is your change request related to a problem? Please describe.** +A clear and concise description of the problem. + +Examples: + +- It's frustrating to have to [...] +- I was looking for a function to [...] + +**Describe the solution you'd like** +A clear and concise description of what a good solution to you looks like, including any solutions you've already considered. + +**Additional context** +Add any other context about the change request here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..8d7661e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: PureScript Discourse + url: https://discourse.purescript.org/ + about: Ask and answer questions on the PureScript discussion forum. + - name: PureScript Discord + url: https://purescript.org/chat + about: Ask and answer questions on the PureScript chat. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d8780f7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +**Description of the change** +Clearly and concisely describe the purpose of the pull request. If this PR relates to an existing issue or change proposal, please link to it. Include any other background context that would help reviewers understand the motivation for this PR. + +--- + +**Checklist:** + +- [ ] Added the change to the changelog's "Unreleased" section with a link to this PR and your username +- [ ] Linked any existing issues or proposals that this pull request should close +- [ ] Updated or added relevant documentation in the README and/or documentation directory +- [ ] Added a test for the contribution (if applicable) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fc54710 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up PureScript toolchain + uses: purescript-contrib/setup-purescript@main + with: + purescript: "unstable" + purs-tidy: "latest" + + - name: Cache PureScript dependencies + uses: actions/cache@v2 + with: + key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }} + path: | + .spago + output + + - name: Set up Node toolchain + uses: actions/setup-node@v2 + with: + node-version: "14.x" + + - name: Cache NPM dependencies + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Install NPM dependencies + run: npm install + + - name: Build the project + run: npm run build + + - name: Run tests + run: npm run test + + - name: Check formatting + run: purs-tidy check src test + + - name: Verify Bower & Pulp + run: | + npm install bower pulp@16.0.0-0 + npx bower install + npx pulp build -- --censor-lib --strict + if [ -d "test" ]; then + npx pulp test + fi diff --git a/.gitignore b/.gitignore index 5544435..6a45203 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,14 @@ -.DS_Store -.psci -.psci_modules -.pulp-cache -npm-debug.log -node_modules/ -bower_components/ -tmp/ -output/ -.psc-ide-port -.psc-package/ +.* +!.gitignore +!.github +!.editorconfig +!.tidyrc.json +!.eslintrc.json + +output +generated-docs +bower_components + +node_modules +package-lock.json +*.lock diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..4f013c1 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "source", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "never", + "width": null +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 968390b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: node_js -dist: trusty -sudo: required -node_js: stable -install: - - npm install -g bower - - npm install - - bower install -script: - - npm run -s build -after_success: -- >- - test $TRAVIS_TAG && - echo $GITHUB_TOKEN | pulp login && - echo y | pulp publish --no-push diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ab7ff21 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,87 @@ +# Changelog + +Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +Breaking changes: + +New features: + +Bugfixes: + +Other improvements: + +## [v8.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v8.0.0) - 2022-04-27 + +Breaking changes: +- Migrate FFI to ES modules (#28 by @JordanMartinez) + +New features: + +Bugfixes: + +Other improvements: +- Added `purs-tidy` formatter (#26 by @thomashoneyman) + +## [v7.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v7.0.0) - 2021-02-26 + +Breaking changes: +- Added support for PureScript 0.14 and dropped support for all previous versions (#20) + +New features: + +Bugfixes: + +Other improvements: +- Changed default branch to `main` from `master` +- Updated to comply with Contributors library guidelines by adding new issue and pull request templates, updating documentation, and migrating to Spago for local development and CI (#18) + +## [v6.1.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v6.1.0) - 2019-05-28 + +New improvements: + +- Added `hydrate` function (#14, @starper) +- Relaxed upper bound on `purescript-react` for v0.13.x compiler compatibility (#17, @hdgarrood) + +## [v6.0.1](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v6.0.1) - 2019-03-10 + +- Updated `web-dom` dependency (#12, @athanclark) + +## [v6.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v6.0.0) - 2018-06-09 + +Breaking changes: + +- Updated for PureScript 0.12 and React 6.0.0 (#11 by @natefaubion) + +## [v5.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v5.0.0) - 2017-12-16 + +Breaking changes: + +- Updated dependencies (#10 by @jvliwanag) + +## [v4.1.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v4.1.0) - 2017-10-11 + +New features: + +- Added `refToNode` (#8, @coot) + +## [v4.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v4.0.0) - 2017-07-11 + +- Updated for the latest `purescript-react` release (@coot) + +## [v3.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v3.0.0) - 2017-04-05 + +- Updated for PureScript 0.11 + +## [v1.0.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v1.0.0) - 2016-06-12 + +- Updated for PureScript compiler versions >= 0.9.1 (@spicydonuts) + +## [v0.2.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v0.2.0) - 2016-03-05 + +- Updated for `purescript-react#v0.7.0` (@kRITZCREEK) + +## [v0.1.0](https://github.com/purescript-contrib/purescript-react-dom/releases/tag/v0.1.0) - 2016-01-25 + +- Initial release compatible with `purescript-react#v0.6.0` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ee46c33 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +# Contributing to React DOM + +Thanks for your interest in contributing to `react-dom`! We welcome new contributions regardless of your level of experience or familiarity with PureScript. + +Every library in the Contributors organization shares a simple handbook that helps new contributors get started. With that in mind, please [read the short contributing guide on purescript-contrib/governance](https://github.com/purescript-contrib/governance/blob/main/contributing.md) before contributing to this library. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5b8ce82 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2017 various contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 525838b..963287c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,49 @@ -# purescript-react-dom +# React DOM -Low-level React DOM bindings for PureScript +[![CI](https://github.com/purescript-contrib/purescript-react-dom/workflows/CI/badge.svg?branch=main)](https://github.com/purescript-contrib/purescript-react-dom/actions?query=workflow%3ACI+branch%3Amain) +[![Release](https://img.shields.io/github/release/purescript-contrib/purescript-react-dom.svg)](https://github.com/purescript-contrib/purescript-react-dom/releases) +[![Pursuit](https://pursuit.purescript.org/packages/purescript-react-dom/badge)](https://pursuit.purescript.org/packages/purescript-react-dom) +[![Maintainer: ethul](https://img.shields.io/badge/maintainer-ethul-teal.svg)](https://github.com/ethul) -- [Module Documentation](https://pursuit.purescript.org/packages/purescript-react-dom/) +Low-level React DOM bindings for PureScript. You may also be interested in the higher-level [`react-basic-dom`](https://github.com/lumihq/purescript-react-basic-dom) library. ## Installation +Install `react-dom` with [Spago](https://github.com/purescript/spago): + +```sh +spago install react-dom ``` -bower install purescript-react-dom + +You will also need to install the `react-dom` package from NPM: + +```sh +npm install --save react-dom ``` -## Example +## Quick start + +The quick start hasn't been written yet (contributions are welcome!). The quick start covers a common, minimal use case for the library, whereas longer examples and tutorials are kept in the [docs directory](./docs). + +## Documentation + +`react-dom` documentation is stored in a few places: + +1. Module documentation is [published on Pursuit](https://pursuit.purescript.org/packages/purescript-react-dom). +2. Written documentation is kept in the [docs directory](./docs). +3. You can use the [react-example](https://github.com/ethul/purescript-react-example) to see these bindings in action. + +If you get stuck, there are several ways to get help: + +- [Open an issue](https://github.com/purescript-contrib/purescript-react-dom/issues) if you have encountered a bug or problem. +- Ask general questions on the [PureScript Discourse](https://discourse.purescript.org) forum or the [PureScript Discord](https://purescript.org/chat) chat. + +## Contributing + +You can contribute to `react-dom` in several ways: + +1. If you encounter a problem or have a question, please [open an issue](https://github.com/purescript-contrib/purescript-react-dom/issues). We'll do our best to work with you to resolve or answer it. + +2. If you would like to contribute code, tests, or documentation, please [read the contributor guide](./CONTRIBUTING.md). It's a short, helpful introduction to contributing to this library, including development instructions. -Please refer to [purescript-react-example](https://github.com/ethul/purescript-react-example) +3. If you have written a library, tutorial, guide, or other resource based on this package, please share it on the [PureScript Discourse](https://discourse.purescript.org)! Writing libraries and learning resources are a great way to help this library succeed. diff --git a/bower.json b/bower.json index ef769ea..d3dc30c 100644 --- a/bower.json +++ b/bower.json @@ -13,11 +13,14 @@ ], "repository": { "type": "git", - "url": "git://github.com/purescript-contrib/purescript-react-dom.git" + "url": "https://github.com/purescript-contrib/purescript-react-dom.git" }, "dependencies": { - "purescript-dom": "^4.5.0", - "purescript-react": "^4.0.0", - "purescript-eff": "^3.1.0" + "purescript-effect": "^4.0.0", + "purescript-react": "^10.0.1", + "purescript-web-dom": "^6.0.0" + }, + "devDependencies": { + "purescript-console": "^6.0.0" } } diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..6a446cf --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# React DOM Documentation + +This directory contains documentation for `react-dom`. If you are interested in contributing new documentation, please read the [contributor guidelines](../CONTRIBUTING.md) and [What Nobody Tells You About Documentation](https://documentation.divio.com) for help getting started. diff --git a/package.json b/package.json index 48b8218..2382bdb 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,14 @@ { "private": true, "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "pulp build" + "build": "eslint src && spago build --purs-args '--censor-lib --strict'", + "test": "spago test --no-install" }, "devDependencies": { - "pulp": "^11.0.0", - "purescript-psa": "^0.5.0", - "purescript": "^0.11.1", - "rimraf": "^2.5.4" + "eslint": "^7.6.0", + "purescript-psa": "^0.8.2" }, "peerDependencies": { - "react-dom": "^15.6.1" + "react-dom": "^16.0.0" } } diff --git a/packages.dhall b/packages.dhall new file mode 100644 index 0000000..582d6d3 --- /dev/null +++ b/packages.dhall @@ -0,0 +1,4 @@ +let upstream = + https://raw.githubusercontent.com/purescript/package-sets/prepare-0.15/src/packages.dhall + +in upstream diff --git a/psc-package.json b/psc-package.json deleted file mode 100644 index bc5211c..0000000 --- a/psc-package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "purescript-react-dom", - "set": "psc-0.10.1", - "source": "https://raw.githubusercontent.com/purescript/package-sets/psc-0.10.1/packages.json", - "depends": [ - "arrays", - "bifunctors", - "control", - "datetime", - "distributive", - "dom", - "eff", - "eff-functions", - "either", - "enums", - "exceptions", - "foldable-traversable", - "foreign", - "functions", - "generics", - "identity", - "integers", - "invariant", - "js-date", - "lazy", - "lists", - "math", - "maybe", - "media-types", - "monoid", - "newtype", - "nonempty", - "nullable", - "partial", - "prelude", - "proxy", - "react", - "st", - "strings", - "tailrec", - "transformers", - "tuples", - "unfoldable", - "unsafe-coerce" - ] -} diff --git a/spago.dhall b/spago.dhall new file mode 100644 index 0000000..1294aae --- /dev/null +++ b/spago.dhall @@ -0,0 +1,14 @@ +{ name = "react-dom" +, dependencies = + [ "console" + , "effect" + , "functions" + , "maybe" + , "nullable" + , "prelude" + , "react" + , "web-dom" + ] +, packages = ./packages.dhall +, sources = [ "src/**/*.purs", "test/**/*.purs" ] +} diff --git a/src/ReactDOM.js b/src/ReactDOM.js index a8913fc..87b77bc 100644 --- a/src/ReactDOM.js +++ b/src/ReactDOM.js @@ -1,16 +1,15 @@ -/* global exports */ -"use strict"; +import ReactDOM from "react-dom"; +import ReactDOMServer from "react-dom/server"; -var ReactDOM = require("react-dom"); -var ReactDOMServer = require("react-dom/server"); +export function renderImpl(element, container) { + return ReactDOM.render(element, container); +} -exports.renderImpl = function (nothing, just, element, container) { - var result = ReactDOM.render(element, container); - return result === null ? nothing : just(result); -}; +export function hydrateImpl(element, container) { + return ReactDOM.hydrate(element, container); +} -exports.unmountComponentAtNodeImpl = ReactDOM.unmountComponentAtNode; -exports.findDOMNodeImpl = ReactDOM.findDOMNode; - -exports.renderToStringImpl = ReactDOMServer.renderToString; -exports.renderToStaticMarkupImpl = ReactDOMServer.renderToStaticMarkup; +export const unmountComponentAtNodeImpl = ReactDOM.unmountComponentAtNode; +export const findDOMNodeImpl = ReactDOM.findDOMNode; +export const renderToStringImpl = ReactDOMServer.renderToString; +export const renderToStaticMarkupImpl = ReactDOMServer.renderToStaticMarkup; diff --git a/src/ReactDOM.purs b/src/ReactDOM.purs index 0ead509..387787f 100644 --- a/src/ReactDOM.purs +++ b/src/ReactDOM.purs @@ -1,38 +1,44 @@ module ReactDOM ( render + , hydrate , unmountComponentAtNode , findDOMNode , renderToString , renderToStaticMarkup - , refToNode ) where -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Uncurried (runEffFn1, EffFn4, EffFn1, runEffFn4) -import DOM (DOM) -import DOM.Node.Types (Element, Node) +import Prelude + import Data.Function.Uncurried (runFn1, Fn1) -import Data.Maybe (Maybe(..)) +import Data.Maybe (Maybe) import Data.Nullable (Nullable, toMaybe) -import React (ReactElement, ReactComponent, Ref) -import Unsafe.Coerce (unsafeCoerce) +import Effect (Effect) +import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2) +import React (ReactElement, ReactComponent) +import Web.DOM.Element (Element) -- | Render a React element in a document element. Returns Nothing for stateless components. render - :: forall eff - . ReactElement + :: ReactElement + -> Element + -> Effect (Maybe ReactComponent) +render rEl el = toMaybe <$> runEffectFn2 renderImpl rEl el + +-- | Same as `render`, but is used to hydrate a container whose HTML contents were rendered on the server. +hydrate + :: ReactElement -> Element - -> Eff (dom :: DOM | eff) (Maybe ReactComponent) -render = runEffFn4 renderImpl Nothing Just + -> Effect (Maybe ReactComponent) +hydrate rEl el = toMaybe <$> runEffectFn2 hydrateImpl rEl el -- | Removes a mounted React element in a document element. -- | Returns true if it was unmounted, false otherwise. -unmountComponentAtNode :: forall eff. Element -> Eff (dom :: DOM | eff) Boolean -unmountComponentAtNode = runEffFn1 unmountComponentAtNodeImpl +unmountComponentAtNode :: Element -> Effect Boolean +unmountComponentAtNode = runEffectFn1 unmountComponentAtNodeImpl -- | Finds the DOM node rendered by the component. -findDOMNode :: forall eff. ReactComponent -> Eff (dom :: DOM | eff) Element -findDOMNode = runEffFn1 findDOMNodeImpl +findDOMNode :: ReactComponent -> Effect Element +findDOMNode = runEffectFn1 findDOMNodeImpl -- | Render a React element as a string. renderToString :: ReactElement -> String @@ -43,35 +49,27 @@ renderToStaticMarkup :: ReactElement -> String renderToStaticMarkup = runFn1 renderToStaticMarkupImpl foreign import renderImpl - :: forall eff - . EffFn4 - (dom :: DOM | eff) - (Maybe ReactComponent) - (ReactComponent -> Maybe ReactComponent) + :: EffectFn2 ReactElement Element - (Maybe ReactComponent) + (Nullable ReactComponent) + +foreign import hydrateImpl + :: EffectFn2 + ReactElement + Element + (Nullable ReactComponent) foreign import unmountComponentAtNodeImpl - :: forall eff - . EffFn1 - (dom :: DOM | eff) + :: EffectFn1 Element Boolean foreign import findDOMNodeImpl - :: forall eff - . EffFn1 - (dom :: DOM | eff) + :: EffectFn1 ReactComponent Element foreign import renderToStringImpl :: Fn1 ReactElement String foreign import renderToStaticMarkupImpl :: Fn1 ReactElement String - -refToNode :: Nullable Ref -> Maybe Node -refToNode ref = toMaybe (coerce ref) - where - coerce :: Nullable Ref -> Nullable Node - coerce = unsafeCoerce diff --git a/test/Main.purs b/test/Main.purs new file mode 100644 index 0000000..f91f98c --- /dev/null +++ b/test/Main.purs @@ -0,0 +1,11 @@ +module Test.Main where + +import Prelude + +import Effect (Effect) +import Effect.Class.Console (log) + +main :: Effect Unit +main = do + log "🍝" + log "You should add some tests."