├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── packages.dhall ├── spago.dhall ├── src ├── Components │ ├── App.purs │ └── Toggle.purs ├── Main.purs ├── index.html └── index.js ├── test └── Main.purs └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | /bower_components/ 2 | /node_modules/ 3 | /.pulp-cache/ 4 | /output/ 5 | /build/ 6 | /generated-docs/ 7 | /.psc-package/ 8 | /.psc* 9 | /.purs* 10 | /.psa* 11 | /.spago 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-basic-starter 2 | 3 | Fork or clone this repo to begin 4 | 5 | ## Install dependencies 6 | 7 | `npm i && npx spago install` 8 | 9 | ## Develop 10 | 11 | `npm start` 12 | 13 | ## Build and deploy (gh-pages) 14 | 15 | `npm run deploy` 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-basic-starter", 3 | "version": "1.0.0", 4 | "description": "An example react-basic project", 5 | "repository": "lumihq/react-basic-starter", 6 | "scripts": { 7 | "start": "webpack-dev-server -wd --port 3000", 8 | "start-public": "webpack-dev-server -wd --port 3000 --host 0.0.0.0", 9 | "build": "webpack -p", 10 | "predeploy": "npm run build", 11 | "deploy": "gh-pages -d build", 12 | "build:purs": "spago build", 13 | "pscid:build": "spago build", 14 | "pscid:test": "spago test", 15 | "test": "spago test", 16 | "clean": "rm -rf output build", 17 | "clean:everything": "npm run clean && rm -rf node_modules .spago" 18 | }, 19 | "dependencies": { 20 | "react": "16.13.1", 21 | "react-dom": "16.13.1" 22 | }, 23 | "devDependencies": { 24 | "clean-webpack-plugin": "^3.0.0", 25 | "css-loader": "3.5.3", 26 | "gh-pages": "2.2.0", 27 | "html-webpack-plugin": "^4.3.0", 28 | "mini-css-extract-plugin": "0.9.0", 29 | "optimize-css-assets-webpack-plugin": "^5.0.3", 30 | "purescript": "^0.13.6", 31 | "purescript-psa": "^0.7.3", 32 | "purs-loader": "^3.7.1", 33 | "resource-hints-webpack-plugin": "0.0.2", 34 | "sass-loader": "8.0.2", 35 | "script-ext-html-webpack-plugin": "^2.1.4", 36 | "source-map-loader": "0.2.4", 37 | "spago": "^0.15.2", 38 | "style-loader": "1.2.1", 39 | "uglifyjs-webpack-plugin": "^2.2.0", 40 | "webpack": "^4.43.0", 41 | "webpack-bundle-analyzer": "^3.7.0", 42 | "webpack-cli": "^3.3.11", 43 | "webpack-dev-server": "^3.11.0", 44 | "webpack-manifest-plugin": "^2.2.0", 45 | "webpack-subresource-integrity": "^1.4.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages.dhall: -------------------------------------------------------------------------------- 1 | {- 2 | Welcome to your new Dhall package-set! 3 | 4 | Below are instructions for how to edit this file for most use 5 | cases, so that you don't need to know Dhall to use it. 6 | 7 | ## Warning: Don't Move This Top-Level Comment! 8 | 9 | Due to how `dhall format` currently works, this comment's 10 | instructions cannot appear near corresponding sections below 11 | because `dhall format` will delete the comment. However, 12 | it will not delete a top-level comment like this one. 13 | 14 | ## Use Cases 15 | 16 | Most will want to do one or both of these options: 17 | 1. Override/Patch a package's dependency 18 | 2. Add a package not already in the default package set 19 | 20 | This file will continue to work whether you use one or both options. 21 | Instructions for each option are explained below. 22 | 23 | ### Overriding/Patching a package 24 | 25 | Purpose: 26 | - Change a package's dependency to a newer/older release than the 27 | default package set's release 28 | - Use your own modified version of some dependency that may 29 | include new API, changed API, removed API by 30 | using your custom git repo of the library rather than 31 | the package set's repo 32 | 33 | Syntax: 34 | Replace the overrides' "{=}" (an empty record) with the following idea 35 | The "//" or "⫽" means "merge these two records and 36 | when they have the same value, use the one on the right:" 37 | ------------------------------- 38 | let overrides = 39 | { packageName = 40 | upstream.packageName // { updateEntity1 = "new value", updateEntity2 = "new value" } 41 | , packageName = 42 | upstream.packageName // { version = "v4.0.0" } 43 | , packageName = 44 | upstream.packageName // { repo = "https://www.example.com/path/to/new/repo.git" } 45 | } 46 | ------------------------------- 47 | 48 | Example: 49 | ------------------------------- 50 | let overrides = 51 | { halogen = 52 | upstream.halogen // { version = "master" } 53 | , halogen-vdom = 54 | upstream.halogen-vdom // { version = "v4.0.0" } 55 | } 56 | ------------------------------- 57 | 58 | ### Additions 59 | 60 | Purpose: 61 | - Add packages that aren't already included in the default package set 62 | 63 | Syntax: 64 | Replace the additions' "{=}" (an empty record) with the following idea: 65 | ------------------------------- 66 | let additions = 67 | { package-name = 68 | { dependencies = 69 | [ "dependency1" 70 | , "dependency2" 71 | ] 72 | , repo = 73 | "https://example.com/path/to/git/repo.git" 74 | , version = 75 | "tag ('v4.0.0') or branch ('master')" 76 | } 77 | , package-name = 78 | { dependencies = 79 | [ "dependency1" 80 | , "dependency2" 81 | ] 82 | , repo = 83 | "https://example.com/path/to/git/repo.git" 84 | , version = 85 | "tag ('v4.0.0') or branch ('master')" 86 | } 87 | , etc. 88 | } 89 | ------------------------------- 90 | 91 | Example: 92 | ------------------------------- 93 | let additions = 94 | { benchotron = 95 | { dependencies = 96 | [ "arrays" 97 | , "exists" 98 | , "profunctor" 99 | , "strings" 100 | , "quickcheck" 101 | , "lcg" 102 | , "transformers" 103 | , "foldable-traversable" 104 | , "exceptions" 105 | , "node-fs" 106 | , "node-buffer" 107 | , "node-readline" 108 | , "datetime" 109 | , "now" 110 | ] 111 | , repo = 112 | "https://github.com/hdgarrood/purescript-benchotron.git" 113 | , version = 114 | "v7.0.0" 115 | } 116 | } 117 | ------------------------------- 118 | -} 119 | 120 | 121 | let upstream = 122 | https://github.com/purescript/package-sets/releases/download/psc-0.13.6-20200507/packages.dhall sha256:9c1e8951e721b79de1de551f31ecb5a339e82bbd43300eb5ccfb1bf8cf7bbd62 123 | 124 | let overrides = {=} 125 | 126 | let additions = {=} 127 | 128 | in upstream // overrides // additions 129 | -------------------------------------------------------------------------------- /spago.dhall: -------------------------------------------------------------------------------- 1 | {- 2 | Welcome to a Spago project! 3 | You can edit this file as you like. 4 | -} 5 | { name = "react-basic-starter" 6 | , dependencies = 7 | [ "console", "effect", "prelude", "psci-support", "react-basic" ] 8 | , packages = ./packages.dhall 9 | , sources = [ "src/**/*.purs", "test/**/*.purs" ] 10 | } 11 | -------------------------------------------------------------------------------- /src/Components/App.purs: -------------------------------------------------------------------------------- 1 | module Components.App where 2 | 3 | import Prelude 4 | 5 | import Components.Toggle (toggle) 6 | import React.Basic (Component, JSX, createComponent, makeStateless) 7 | import React.Basic.DOM as R 8 | 9 | component :: Component Unit 10 | component = createComponent "App" 11 | 12 | app :: JSX 13 | app = unit # makeStateless component \_ -> 14 | R.div_ 15 | [ R.h1_ [ R.text "Hello world" ] 16 | , toggle { initialValue: true } 17 | , toggle { initialValue: false } 18 | ] 19 | -------------------------------------------------------------------------------- /src/Components/Toggle.purs: -------------------------------------------------------------------------------- 1 | module Components.Toggle where 2 | 3 | import Prelude 4 | 5 | import Data.Maybe (Maybe(..), fromMaybe) 6 | import React.Basic (Component, JSX, createComponent, make) 7 | import React.Basic.DOM as R 8 | import React.Basic.DOM.Events (capture_) 9 | 10 | type Props = 11 | { initialValue :: Boolean 12 | } 13 | 14 | data Action 15 | = Toggle 16 | 17 | component :: Component Props 18 | component = createComponent "App" 19 | 20 | toggle :: Props -> JSX 21 | toggle = make component { initialState, render } 22 | where 23 | initialState = 24 | Nothing 25 | 26 | render self = 27 | let 28 | on = fromMaybe self.props.initialValue self.state 29 | in 30 | R.button 31 | { onClick: capture_ do 32 | self.setState (Just <<< not <<< fromMaybe on) 33 | , children: 34 | [ R.text 35 | if on 36 | then "On" 37 | else "Off" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/Main.purs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Prelude 4 | 5 | import Components.App (app) 6 | import Data.Maybe (Maybe(..)) 7 | import Effect (Effect) 8 | import Effect.Exception (throw) 9 | import React.Basic.DOM (render) 10 | import Web.DOM.NonElementParentNode (getElementById) 11 | import Web.HTML (window) 12 | import Web.HTML.HTMLDocument (toNonElementParentNode) 13 | import Web.HTML.Window (document) 14 | 15 | main :: Effect Unit 16 | main = do 17 | root <- getElementById "root" =<< (map toNonElementParentNode $ document =<< window) 18 | case root of 19 | Nothing -> throw "Root element not found." 20 | Just r -> render app r 21 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Basic Starter 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | require("./Main.purs").main(); 2 | -------------------------------------------------------------------------------- /test/Main.purs: -------------------------------------------------------------------------------- 1 | module Test.Main where 2 | 3 | import Prelude 4 | import Effect (Effect) 5 | import Effect.Console (log) 6 | 7 | main :: Effect Unit 8 | main = do 9 | log "You should add some tests." 10 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 3 | const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); 4 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 5 | const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); 6 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 7 | const ResourceHintWebpackPlugin = require("resource-hints-webpack-plugin"); 8 | const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin"); 9 | const SubresourceIntegrityWebpackPlugin = require("webpack-subresource-integrity"); 10 | const ManifestPlugin = require("webpack-manifest-plugin"); 11 | const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); 12 | 13 | const outputPath = "build"; 14 | 15 | const isProd = ({ mode }) => mode === "production"; 16 | 17 | module.exports = (_env, options) => ({ 18 | entry: { 19 | main: "./src/index.js", 20 | }, 21 | output: { 22 | filename: "bundle.js", 23 | path: path.resolve(__dirname, outputPath), 24 | crossOriginLoading: "anonymous" 25 | }, 26 | optimization: { 27 | minimizer: [ 28 | new UglifyJsPlugin({ 29 | cache: true, 30 | parallel: true 31 | }), 32 | new OptimizeCSSAssetsPlugin({ 33 | cssProcessorOptions: { 34 | safe: true 35 | } 36 | }) 37 | ], 38 | moduleIds: isProd(options) ? "size" : "named", 39 | splitChunks: { 40 | chunks: "all", 41 | cacheGroups: { 42 | styles: { 43 | name: "styles", 44 | test: /\.(css)$/, 45 | chunks: "all", 46 | enforce: true 47 | }, 48 | react: { 49 | name: "react", 50 | test: /[\\/]node_modules\/(react|react-dom)[\\/]/, 51 | chunks: "all", 52 | priority: -5 53 | }, 54 | purs: { 55 | name: "purs", 56 | test: /[\\/]output[\\/]/, 57 | minChunks: 2, 58 | priority: -5 59 | }, 60 | default: { 61 | minChunks: 2, 62 | priority: -20 63 | } 64 | } 65 | } 66 | }, 67 | plugins: [ 68 | new CleanWebpackPlugin(), 69 | new MiniCssExtractPlugin({ 70 | filename: "styles.css" 71 | }), 72 | new HtmlWebpackPlugin({ 73 | template: "src/index.html" 74 | }), 75 | new ResourceHintWebpackPlugin(), 76 | new ScriptExtHtmlWebpackPlugin({ 77 | defaultAttribute: "async" 78 | }), 79 | new SubresourceIntegrityWebpackPlugin({ 80 | hashFuncNames: ["sha256", "sha384"], 81 | // this is here as an example, comes at a perf cost so 82 | // we probably only want to use it for 3rd party scripts 83 | enabled: false // isProd(options) 84 | }), 85 | new ManifestPlugin(), 86 | new BundleAnalyzerPlugin({ 87 | analyzerMode: isProd(options) ? "static" : "disabled", 88 | openAnalyzer: false 89 | }) 90 | ], 91 | module: { 92 | rules: [ 93 | { 94 | test: /\.js$/, 95 | loader: "source-map-loader", 96 | exclude: /node_modules/ 97 | }, 98 | { 99 | oneOf: [ 100 | { 101 | test: /\.(css)$/, 102 | use: [ MiniCssExtractPlugin.loader, "css-loader" ] 103 | }, 104 | { 105 | test: /\.purs$/, 106 | exclude: /node_modules/, 107 | use: { 108 | loader: "purs-loader", 109 | options: { 110 | src: [ 111 | 'src/**/*.purs' 112 | ], 113 | spago: true, 114 | watch: !isProd(options), 115 | pscIde: true 116 | } 117 | } 118 | } 119 | ] 120 | } 121 | ] 122 | } 123 | }); 124 | --------------------------------------------------------------------------------