diff --git a/examples/with-reasonml/.gitignore b/examples/with-reasonml/.gitignore new file mode 100644 index 00000000..ac1fa842 --- /dev/null +++ b/examples/with-reasonml/.gitignore @@ -0,0 +1,2 @@ +bs +.merlin diff --git a/examples/with-reasonml/README.md b/examples/with-reasonml/README.md new file mode 100644 index 00000000..b9484810 --- /dev/null +++ b/examples/with-reasonml/README.md @@ -0,0 +1,32 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-reasonml) +# Example app using ReasonML & ReasonReact components + +## How to use + +Download the example [or clone the repo](https://github.com/zeit/next.js): + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-reasonml +cd with-reasonml +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) + +```bash +now +``` + +## The idea behind the example + +This example features: + +* An app that mixes together JavaScript and ReasonML components and functions +* An app with two pages which has a common Counter component +* That Counter component maintain the counter inside its module. This is used primarily to illustrate that modules get initialized once and their state variables persist in runtime diff --git a/examples/with-reasonml/bsconfig.json b/examples/with-reasonml/bsconfig.json new file mode 100644 index 00000000..323cfbe3 --- /dev/null +++ b/examples/with-reasonml/bsconfig.json @@ -0,0 +1,7 @@ +{ + "name": "with-reasonml", + "sources": ["components", "pages"], + "bs-dependencies": ["reason-react"], + "reason": { "react-jsx": 2 }, + "package-specs": ["commonjs"] +} diff --git a/examples/with-reasonml/components/Counter.re b/examples/with-reasonml/components/Counter.re new file mode 100644 index 00000000..3ba4ba61 --- /dev/null +++ b/examples/with-reasonml/components/Counter.re @@ -0,0 +1,14 @@ +let component = ReasonReact.statefulComponent "Counter"; +let make _children => { + ...component, + initialState: fun () => 0, + render: fun self => { + let countMsg = "Count: " ^ (string_of_int self.state); + let onClick _evt {ReasonReact.state} => ReasonReact.Update (state + 1); + +
+

(ReasonReact.stringToElement countMsg)

+ +
+ } +}; diff --git a/examples/with-reasonml/components/Header.re b/examples/with-reasonml/components/Header.re new file mode 100644 index 00000000..59014230 --- /dev/null +++ b/examples/with-reasonml/components/Header.re @@ -0,0 +1,13 @@ +let component = ReasonReact.statelessComponent "Header"; +let styles = ReactDOMRe.Style.make + marginRight::"10px" + (); +let make _children => { + ...component, + render: fun _self => { +
+ (ReasonReact.stringToElement "Home") + (ReasonReact.stringToElement "About") +
+ } +}; diff --git a/examples/with-reasonml/index.js b/examples/with-reasonml/index.js new file mode 100644 index 00000000..e69de29b diff --git a/examples/with-reasonml/lib/js/components/counter.js b/examples/with-reasonml/lib/js/components/counter.js new file mode 100644 index 00000000..a1955920 --- /dev/null +++ b/examples/with-reasonml/lib/js/components/counter.js @@ -0,0 +1,31 @@ +// Generated by BUCKLESCRIPT VERSION 1.8.1, PLEASE EDIT WITH CARE +'use strict'; + +var Block = require("bs-platform/lib/js/block.js"); +var Curry = require("bs-platform/lib/js/curry.js"); +var React = require("react"); +var Pervasives = require("bs-platform/lib/js/pervasives.js"); +var ReasonReact = require("reason-react/lib/js/src/reasonReact.js"); + +var component = ReasonReact.statefulComponent("Counter"); + +function make() { + var newrecord = component.slice(); + newrecord[/* render */9] = (function (self) { + var countMsg = "Count: " + Pervasives.string_of_int(self[/* state */3]); + var onClick = function (_, param) { + return /* Update */Block.__(0, [param[/* state */3] + 1 | 0]); + }; + return React.createElement("div", undefined, React.createElement("p", undefined, countMsg), React.createElement("button", { + onClick: Curry._1(self[/* update */2], onClick) + }, "Add")); + }); + newrecord[/* initialState */10] = (function () { + return 0; + }); + return newrecord; +} + +exports.component = component; +exports.make = make; +/* component Not a pure module */ diff --git a/examples/with-reasonml/lib/js/components/header.js b/examples/with-reasonml/lib/js/components/header.js new file mode 100644 index 00000000..c01dd1d1 --- /dev/null +++ b/examples/with-reasonml/lib/js/components/header.js @@ -0,0 +1,30 @@ +// Generated by BUCKLESCRIPT VERSION 1.8.1, PLEASE EDIT WITH CARE +'use strict'; + +var React = require("react"); +var ReasonReact = require("reason-react/lib/js/src/reasonReact.js"); + +var component = ReasonReact.statelessComponent("Header"); + +var styles = { + marginRight: "10px" +}; + +function make() { + var newrecord = component.slice(); + newrecord[/* render */9] = (function () { + return React.createElement("div", undefined, React.createElement("a", { + style: styles, + href: "/" + }, "Home"), React.createElement("a", { + style: styles, + href: "/about" + }, "About")); + }); + return newrecord; +} + +exports.component = component; +exports.styles = styles; +exports.make = make; +/* component Not a pure module */ diff --git a/examples/with-reasonml/lib/js/components/link.js b/examples/with-reasonml/lib/js/components/link.js new file mode 100644 index 00000000..ec24595f --- /dev/null +++ b/examples/with-reasonml/lib/js/components/link.js @@ -0,0 +1,17 @@ +// Generated by BUCKLESCRIPT VERSION 1.8.1, PLEASE EDIT WITH CARE +'use strict'; + +var Link = require("next/link"); +var Js_boolean = require("bs-platform/lib/js/js_boolean.js"); +var ReasonReact = require("reason-react/lib/js/src/reasonReact.js"); + +function make(href, $staropt$star, children) { + var prefetch = $staropt$star ? $staropt$star[0] : /* false */0; + return ReasonReact.wrapJsForReason(Link, { + prefetch: Js_boolean.to_js_boolean(prefetch), + href: href + }, children); +} + +exports.make = make; +/* next/link Not a pure module */ diff --git a/examples/with-reasonml/lib/js/pages/about.js b/examples/with-reasonml/lib/js/pages/about.js new file mode 100644 index 00000000..a4f01381 --- /dev/null +++ b/examples/with-reasonml/lib/js/pages/about.js @@ -0,0 +1,26 @@ +// Generated by BUCKLESCRIPT VERSION 1.8.1, PLEASE EDIT WITH CARE +'use strict'; + +var React = require("react"); +var Header = require("../components/header.js"); +var Counter = require("../components/counter.js"); +var ReasonReact = require("reason-react/lib/js/src/reasonReact.js"); + +var component = ReasonReact.statelessComponent("About"); + +function make() { + var newrecord = component.slice(); + newrecord[/* render */9] = (function () { + return React.createElement("div", undefined, ReasonReact.element(/* None */0, /* None */0, Header.make(/* array */[])), React.createElement("p", undefined, "This is the about page."), ReasonReact.element(/* None */0, /* None */0, Counter.make(/* array */[]))); + }); + return newrecord; +} + +var jsComponent = ReasonReact.wrapReasonForJs(component, (function () { + return make(/* array */[]); + })); + +exports.component = component; +exports.make = make; +exports.jsComponent = jsComponent; +/* component Not a pure module */ diff --git a/examples/with-reasonml/lib/js/pages/index.js b/examples/with-reasonml/lib/js/pages/index.js new file mode 100644 index 00000000..d2013a08 --- /dev/null +++ b/examples/with-reasonml/lib/js/pages/index.js @@ -0,0 +1,26 @@ +// Generated by BUCKLESCRIPT VERSION 1.8.1, PLEASE EDIT WITH CARE +'use strict'; + +var React = require("react"); +var Header = require("../components/header.js"); +var Counter = require("../components/counter.js"); +var ReasonReact = require("reason-react/lib/js/src/reasonReact.js"); + +var component = ReasonReact.statelessComponent("Index"); + +function make() { + var newrecord = component.slice(); + newrecord[/* render */9] = (function () { + return React.createElement("div", undefined, ReasonReact.element(/* None */0, /* None */0, Header.make(/* array */[])), React.createElement("p", undefined, "HOME PAGE is here!"), ReasonReact.element(/* None */0, /* None */0, Counter.make(/* array */[]))); + }); + return newrecord; +} + +var jsComponent = ReasonReact.wrapReasonForJs(component, (function () { + return make(/* array */[]); + })); + +exports.component = component; +exports.make = make; +exports.jsComponent = jsComponent; +/* component Not a pure module */ diff --git a/examples/with-reasonml/package.json b/examples/with-reasonml/package.json new file mode 100644 index 00000000..c1b47483 --- /dev/null +++ b/examples/with-reasonml/package.json @@ -0,0 +1,26 @@ +{ + "name": "with-reasonml", + "version": "1.0.0", + "scripts": { + "dev": "next -w", + "build": "next build", + "start": "next start -w" + }, + "license": "ISC", + "dependencies": { + "babel-plugin-bucklescript": "^0.2.3-1", + "bs-platform": "^1.8.1", + "next": "^2.4.7", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "reason-react": "^0.2.3" + }, + "babel": { + "presets": [ + "next/babel" + ], + "plugins": [ + "babel-plugin-bucklescript" + ] + } +} diff --git a/examples/with-reasonml/pages/About.re b/examples/with-reasonml/pages/About.re new file mode 100644 index 00000000..30e44f1b --- /dev/null +++ b/examples/with-reasonml/pages/About.re @@ -0,0 +1,15 @@ +let component = ReasonReact.statelessComponent "About"; +let make _children => { + ...component, + render: fun _self => { +
+
+

(ReasonReact.stringToElement "This is the about page.")

+ +
+ } +}; +let jsComponent = + ReasonReact.wrapReasonForJs + ::component + (fun _jsProps => make [||]) diff --git a/examples/with-reasonml/pages/Index.re b/examples/with-reasonml/pages/Index.re new file mode 100644 index 00000000..bcf49c31 --- /dev/null +++ b/examples/with-reasonml/pages/Index.re @@ -0,0 +1,15 @@ +let component = ReasonReact.statelessComponent "Index"; +let make _children => { + ...component, + render: fun _self => { +
+
+

(ReasonReact.stringToElement "HOME PAGE is here!")

+ +
+ } +}; +let jsComponent = + ReasonReact.wrapReasonForJs + ::component + (fun _jsProps => make [||]) diff --git a/examples/with-reasonml/pages/about.js b/examples/with-reasonml/pages/about.js new file mode 100644 index 00000000..b08913f1 --- /dev/null +++ b/examples/with-reasonml/pages/about.js @@ -0,0 +1,4 @@ +import { jsComponent as About } from './about.re' +import React from 'react' + +export default () => diff --git a/examples/with-reasonml/pages/index.js b/examples/with-reasonml/pages/index.js new file mode 100644 index 00000000..1cedf538 --- /dev/null +++ b/examples/with-reasonml/pages/index.js @@ -0,0 +1,3 @@ +import { jsComponent as Index } from './index.re' + +export default () => diff --git a/examples/with-redux-code-splitting/README.md b/examples/with-redux-code-splitting/README.md new file mode 100644 index 00000000..2a8a204e --- /dev/null +++ b/examples/with-redux-code-splitting/README.md @@ -0,0 +1,32 @@ + +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-redux-code-splitting) + +# Redux with code splitting example + +## How to use + +Download the example [or clone the repo](https://github.com/zeit/next.js): + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-redux-code-splitting +cd with-redux-code-splitting +``` + +Install it and run: + +```bash +npm install +npm run dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) + +```bash +now +``` + +## The idea behind the example + +Redux uses single store per application and usually it causes problems for code splitting when you want to load actions and reducers used on the current page only. + +This example utilizes [fast-redux](https://github.com/dogada/fast-redux) to split Redux's actions and reducers across pages. In result each page's javascript bundle contains only code that is used on the page. When user navigates to a new page, its actions and reducers are connected to the single shared application store. \ No newline at end of file diff --git a/examples/with-redux-code-splitting/config/redux.js b/examples/with-redux-code-splitting/config/redux.js new file mode 100644 index 00000000..0b9f5b94 --- /dev/null +++ b/examples/with-redux-code-splitting/config/redux.js @@ -0,0 +1,12 @@ +import { createStore, applyMiddleware } from 'redux' +import { composeWithDevTools } from 'redux-devtools-extension' +import thunkMiddleware from 'redux-thunk' +import withRedux from 'next-redux-wrapper' +import { rootReducer } from 'fast-redux' + +export const initStore = (initialState = {}) => { + return createStore(rootReducer, initialState, + composeWithDevTools(applyMiddleware(thunkMiddleware))) +} + +export const reduxPage = (comp) => withRedux(initStore)(comp) diff --git a/examples/with-redux-code-splitting/containers/about.js b/examples/with-redux-code-splitting/containers/about.js new file mode 100644 index 00000000..5813b640 --- /dev/null +++ b/examples/with-redux-code-splitting/containers/about.js @@ -0,0 +1,32 @@ +import React from 'react' +import {bindActionCreators} from 'redux' +import {connect} from 'react-redux' +import {namespaceConfig} from 'fast-redux' +import Link from 'next/link' + +const DEFAULT_STATE = {version: 1} + +const {actionCreator, getState: getAboutState} = namespaceConfig('about', DEFAULT_STATE) + +const bumpVersion = actionCreator(function bumpVersion (state, increment) { + return {...state, version: state.version + increment} +}) + +const About = ({ version, bumpVersion }) => ( +
+

About us

+

Current version: {version}

+

+ Homepage +
+) + +function mapStateToProps (state) { + return getAboutState(state, 'version') +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ bumpVersion }, dispatch) +} + +export default connect(mapStateToProps, mapDispatchToProps)(About) diff --git a/examples/with-redux-code-splitting/containers/homepage.js b/examples/with-redux-code-splitting/containers/homepage.js new file mode 100644 index 00000000..50eb6384 --- /dev/null +++ b/examples/with-redux-code-splitting/containers/homepage.js @@ -0,0 +1,32 @@ +import React from 'react' +import {bindActionCreators} from 'redux' +import {connect} from 'react-redux' +import {namespaceConfig} from 'fast-redux' +import Link from 'next/link' + +const DEFAULT_STATE = {build: 1} + +const {actionCreator, getState: getHomepageState} = namespaceConfig('homepage', DEFAULT_STATE) + +const bumpBuild = actionCreator(function bumpBuild (state, increment) { + return {...state, build: state.build + increment} +}) + +const Homepage = ({ build, bumpBuild }) => ( +
+

Homepage

+

Current build: {build}

+

+ About Us +
+) + +function mapStateToProps (state) { + return getHomepageState(state) +} + +function mapDispatchToProps (dispatch) { + return bindActionCreators({ bumpBuild }, dispatch) +} + +export default connect(mapStateToProps, mapDispatchToProps)(Homepage) diff --git a/examples/with-redux-code-splitting/package.json b/examples/with-redux-code-splitting/package.json new file mode 100644 index 00000000..96d2be9d --- /dev/null +++ b/examples/with-redux-code-splitting/package.json @@ -0,0 +1,22 @@ +{ + "name": "with-redux-code-splitting", + "version": "1.0.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "fast-redux": "~0.3.0", + "next": "latest", + "next-redux-wrapper": "~1.3.2", + "react": "~15.6.1", + "react-dom": "~15.6.1", + "react-redux": "~5.0.5", + "redux": "~3.7.2", + "redux-devtools-extension": "~2.13.2", + "redux-thunk": "~2.2.0" + }, + "author": "Dmytro V. Dogadailo (https://dogada.org)", + "license": "ISC" +} diff --git a/examples/with-redux-code-splitting/pages/about.js b/examples/with-redux-code-splitting/pages/about.js new file mode 100644 index 00000000..0237f9e5 --- /dev/null +++ b/examples/with-redux-code-splitting/pages/about.js @@ -0,0 +1,4 @@ +import {reduxPage} from '../config/redux' +import About from '../containers/about' + +export default reduxPage(About) diff --git a/examples/with-redux-code-splitting/pages/index.js b/examples/with-redux-code-splitting/pages/index.js new file mode 100644 index 00000000..4e1a993d --- /dev/null +++ b/examples/with-redux-code-splitting/pages/index.js @@ -0,0 +1,4 @@ +import {reduxPage} from '../config/redux' +import Homepage from '../containers/homepage' + +export default reduxPage(Homepage) diff --git a/examples/with-relay-modern/.env b/examples/with-relay-modern/.env index d98d836f..d8a2a35d 100644 --- a/examples/with-relay-modern/.env +++ b/examples/with-relay-modern/.env @@ -1 +1 @@ -RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example \ No newline at end of file +RELAY_ENDPOINT=https://api.graph.cool/relay/v1/next-js-with-relay-modern-example diff --git a/package.json b/package.json index 1a2ee220..7134d9eb 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "standard": { "parser": "babel-eslint", "ignore": [ - "**/node_modules/**" + "**/node_modules/**", + "**/examples/**/lib/**" ] }, "lint-staged": { diff --git a/server/document.js b/server/document.js index bf441f79..402c105f 100644 --- a/server/document.js +++ b/server/document.js @@ -96,14 +96,19 @@ export class Head extends Component { } export class Main extends Component { + static propTypes = { + className: PropTypes.string + } + static contextTypes = { _documentProps: PropTypes.any } render () { const { html, errorHtml } = this.context._documentProps + const { className } = this.props return ( -
+