Skip to content
This repository has been archived by the owner on Jan 26, 2020. It is now read-only.

Commit

Permalink
published v0.6 and updated docs for breaking changes
Browse files Browse the repository at this point in the history
  • Loading branch information
alidcast committed Jul 11, 2018
1 parent 3175e2b commit ce69d62
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 117 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
package-lock.json
.rogue
build
build
dist
47 changes: 29 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
# Rogue

With Rogue, the SSR configuration will be nearly invisible to you. You don't need a special `/pages` directory (like Nextjs) or a separate `routes.js` file (like Afterjs). Only the `App.js` entry point you'd usually have. This means that you can wrap your app in layouts/transitions/providers, etc. the same way you would in a regular React Application, and staying true to React's values, you can organize your code however you like.
With Rogue, the server rendering configuration will be nearly invisible to you. You don't need a special `/pages` directory (like Nextjs) or a separate `routes.js` file (like Afterjs). All you need is the `App.js` entry point you'd usually have. This means that you can wrap your app in layouts/transitions/providers, etc. the same way you would in a regular React Application, and staying true to React's values, you can organize your code however you like.

As an added benefit, Rogue also comes with first-class support for: Graghql (Apollo), State Mangagement (Redux), and Css-in-Js (Emotion / Styled-Components).
As an added benefit, Rogue also comes with first-class support for: GraphQL (Apollo), State Management (Redux), and Css-in-Js (Emotion / Styled-Components).

But anyway, -- these are just words; and we know the way to your heart is with code, so here's an example of how your server-rendered apps will look going forward:

```js
// App.js
export default () => 'Hello World!'

// client.js
import { hydrate } from '@roguejs/app'
import App from './App'

hydrate(App)

// server.js
import Rogue from '@roguejs/app/server'
import http from 'http'
import App from './App'

const app = new Rogue(App)

http.createServer(app.render).listen(3000)
```

🚧 Under Construction 🚧

Expand All @@ -17,38 +39,27 @@ higher order components that come preconfigured with SSR support for Rogue. You

Each of the above packages holds its respective documentation inside its `README.md`.

### Table of Contents
### Index

- [Rogue Framework](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app)
- [App Setup](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#app-setup)
- [App Concepts](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#app-concepts)
- [Data Fetching and Middleware](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#data-fetching-and-middleware)
- [Providers, Layouts, Pages, etc.](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#providers-layouts-pages-etc)
- [Server-rendering Logic](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#server-rendering-logic)
- [App Enhacements](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#app-enhancements)
- [Document Tags](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#document-tags)
- [Code Splitting](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#document-tags)
- [Custom Enhancements](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#custom-enhancements)
- [Custom Server](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#custom-server)
- [Rogue Hocs](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-hocs)
- [CSS-in-JS](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-hocs#css-in-js)
- [State Management](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-hocs#state-management)
- [Apollo Graphql](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-hocs#apollo-graphql)

### FAQ

#### How come you don't need any upfront route configuration anymore?

Since we assume you're using React Router 4 (why wouldn't you be?!), we can walk your component tree and use the same logic as your router to know which routes will be called so that we can handle SSR for them.

Read the docs on [walking your `App.js` tree](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-app#walking-your-app-tree) for an in-depth explanation.

#### Will Rogue support static pages like `Gatsbyjs` or `Nextjs`?

Rouge was created to make complex apps simpler. We leverage RR4 so that we can infer from your code what routes to dynamically SSR without any configuration. However, if you only need static pages, then Nextjs and Gatsby are great solutions, and having a `/pages` directory might even fit your use-case better.

## Author

- Alid Castano ([@alidcastano](https://twitter.com/alidcastano))

## Inspiration
## Inspiration

- [Nextjs](https://github.com/zeit/next.js/)
- [Razzle](https://github.com/jaredpalmer/razzle)+[Afterjs](https://github.com/jaredpalmer/after.js)
Expand Down
132 changes: 37 additions & 95 deletions packages/rogue-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

- [App Setup](#app-setup)
- [App Concepts](#app-concepts)
- [Data Fetching and Middleware](#packages/rogue-app#data-fetching-and-middleware)
- [Server-rendering logic](#packages/rogue-app#server-rendering-logic)
- [`getInitialProps`](#getinitialprops-ctx--data--void)
- [Providers, Layouts, Pages, etc.](#providers-layouts-pages-etc)
- [Walking your App tree](#walking-your-app-tree)
- [App Enhancements](#app-enhancements)
- [Document Tags](#document-tags)
- [Code Splitting](#code-splitting)
Expand All @@ -30,10 +28,7 @@ import { Helmet } from 'react-helmet'
import serveStatic from 'serve-static'
import App from './App'

const rogue = new Rogue({
Helmet,
App
})
const rogue = new Rogue(App)

rogue.preuse(serveStatic(process.env.PUBLIC_DIR))

Expand Down Expand Up @@ -64,11 +59,9 @@ For an example of setting up Rogue with your own build system, check out the [wi

## `rogue` API

* `rogue(args: Object)`
* `rogue(App: React.Component, options: Object)`

Accepts the following arguments:
* `Helmet`: (required) A `react-helmet` component.
* `App`: (required) Your app's root component.
Accepts the following options:
* `bundleUrl`: The location where your bundle will be served from. Defaults to `./bundle.js`.

Has the following methods:
Expand All @@ -79,32 +72,34 @@ Has the following methods:

## App Concepts

### Data Fetching and Middleware
### Server-rendering logic

Any logic you'd like to handle on initial client and server rendering can be done inside a component's `static getInitialProps` method (we kept the same property name as `Nextjs` to pay homeage to the grandaddy of React SSR frameworks).
Any logic you'd like to handle upon server rendering can be done inside a component's `static getInitialProps` method (we kept the same property name as `Nextjs` to pay homeage to the grandaddy of React SSR frameworks).

You can use this property to prefetch data:
It's important to note that Rogue only calls `getInitialProps` inside your `App.js` component (and not inside `pages` like Nextjs).

```js
export default class App extends React.Component {
static async getInitialProps({ req, res }) {
const data = await callMyApi()
return data
}
}
```
The reason for that is that Rogue assumes you're using Apollo Graphql and React Router 4. So that elimates the two primary use-cases for `getInitialProps` inside pages: querying data and handling redirects.

Or to handle route middleware:
However, `getInitialProps` is still useful for bootstrapping your application with server specific. For example, here's how you might handle refreshing an authenticated user:

```js
export default class Route extends React.Component {
static getInitialProps({ req, res, redirect }) {
if (req.url === '/not-allowed') redirect('/')
// note: this example uses the Apollo and Redux Hocs
export default class App extends React.Component {
static async getInitialProps({ req, res }) {
const token = getToken(ctx.req)
if (!ctx.store.getState().session.user) { // refresh session
try {
const res = await ctx.apollo.query({ query: authUserQuery })
ctx.store.dispatch(setSession({ user: res.data.authUser }))
} catch (err) { // invalid or expired token
ctx.store.dispatch(removeSession())
}
}
}
}
```

Make sure that if you do return any value from `getInitialProps`, that it is a plain `Object`, as it will be serialized when server rendering.
If you return any value from `getInitialProps`, make sure that it is a plain `Object`, as it will be serialized when server rendering.

This data will then be passed to the component exported from your `App.js` file.

Expand All @@ -125,60 +120,6 @@ This data will then be passed to the component exported from your `App.js` file.
- `hash`: The hash of the current route (with the `#`), if it has one. If no hash is present the value will be an empty string.
- `fullPath`: The full resolved URL including query and hash.

### Providers, Layouts, Pages, etc.

Remember that Rogue doesn't ask you to configure any routes upfront. You only need an `App.js` component and make sure to use React Router 4 (RR4). We'll walk your component tree and use the same logic as your router to know which routes to server render.

How do you handle Providers, Layouts, and Pages in your application with just an `App.js` file? That's the wonderful simplicity of Rogue: you're just using React, React Router 4, and some optional `getInitialProps` magic.

Let us briefly explain how we server render your application so that you can better understand how to handle this yourself.

#### Walking your App tree

Starting from the component exported from your `App.js`, Rogue will walk your component tree looking for any components with a `static getInitialProps` method. It'll load these components in the order they are declared.

Most applications are usually organized in this order:

```
Providers (e.g. ApolloProvider, StyleProvider) -> Layouts (e.g. AppLayout, AuthLayout) -> Pages (e.g. Dashboard, Login, Register)
```

`Providers` are regular components with an optional `getInitialProps` method. For examples of this check out [App Customization](#app-customization) section or the code found inside the `rogue/hocs` directory.

`Layouts` and `Pages` on the other hand, are more properly thought of as "routes," that also can optionally have a `getInitialProps` method. For Rogue, the only difference between the two is that one comes before the other.

This is the important part to remember: since you can only server render routes exclusively (e.g. you match `/route1` or `/route2`, not both), Rogue expects you to configure your Layouts and Pages in that manner. The way you do that is with a RR4's [Switch component](https://reacttraining.com/react-router/web/api/Switch).

Here's an example:

```js
// App.js
import { Switch, Route } from 'react-router-dom'

export default () => (
<Provider>
<Switch>
// Route with only a Page
<Route exact path="/welcome" component={WelcomePage} />
// Routes with a Layout and a Page, via render props
<Route exact path="/register" render={props => <AuthLayout><Register {...props}></AuthLayout>} />
<Route exact path="/login" render={props => <AuthLayout><Login {...props}></AuthLayout>} />
// Route with a Layout and a Page, via a nested switch statement
<Route path="/" render={props => {
<AppLayout>
<Switch>
<Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/profile" component={Profile} />
</Switch>
</AppLayout>}
}/>
</Switch>
</Provider>
)
```

How does Rogue prevent itself from walking your entire App.js tree? After we find your first switch block (i.e. an exclusively rendered Page), we'll continue walking until we find five consecutive components without an `getInitialProps` method. We found this heuristic to work extremely well—there's no reason why you wouldn't have at least one `Switch` block (this isn't a SPA mate), or need to nest a servable component more than five levels apart. And the tiny performance cost of walking your component tree is well worth the simplicity it buys your application.

## App Enhancements

Rogue makes it easy to enhance your app with functionality.
Expand Down Expand Up @@ -249,26 +190,27 @@ As an example, here's how we configure SSR for or [`emotion` hoc](https://github

```js
RogueEmotionProvider.getInitialProps = (ctx) => {
if (ctx.isServer) {
ctx.app.markupRenderers.push( // add renderer function to include styles in html markup
markup => require('emotion-server').renderStylesToString(markup)
)
}
ctx.app.markupRenderers.push(
markup => require('emotion-server').renderStylesToString(markup)
)

let props = {}
if (App.getInitialProps) props = await App.getInitialProps(ctx) || {}
return props
}
```

And here's how we do it for our [`styled-components` hoc](https://github.com/alidcastano/rogue.js/tree/master/packages/rogue-hocs/styled-components):

```js
RogueStyledProvider.getInitialProps = (ctx) => {
if (ctx.isServer) {
const { ServerStyleSheet } = require('styled-components')
const sheet = new ServerStyleSheet()
sheet.collectStyles(ctx.app.Component)
ctx.app.headTags.push( // add style tags to head
sheet.getStyleTags()
)
}
const sheet = new ServerStyleSheet()
sheet.collectStyles(ctx.app.Component)
ctx.app.headTags.push(sheet.getStyleTags())

let props = {}
if (App.getInitialProps) props = await App.getInitialProps(ctx) || {}
return props
}
```

Expand All @@ -281,7 +223,7 @@ import Rogue from '@roguejs/app/server'
import express from 'express'
import App from './app/App'

const rogue = new Rogue({...})
const rogue = new Rogue(App)

const app = express()

Expand Down
2 changes: 1 addition & 1 deletion packages/rogue-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@roguejs/app",
"version": "0.5.1",
"version": "0.6.0",
"description": "A SSR experience for React that is quick and invisible",
"repository": "https://github.com/alidcastano/rogue/blob/master/packages/rogue-app",
"author": "Alid Castano",
Expand Down
2 changes: 1 addition & 1 deletion packages/rogue-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@roguejs/cli",
"version": "0.4.0",
"version": "0.6.0",
"repository": "https://github.com/alidcastano/rogue/tree/master/packages/rogue-cli",
"author": "Alid Castano",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/rogue-hocs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@roguejs/hocs",
"version": "0.4.8",
"version": "0.6.0",
"repository": "https://github.com/alidcastano/rogue/tree/master/packages/rogue-hocs",
"author": "Alid Castano",
"license": "MIT",
Expand Down

0 comments on commit ce69d62

Please sign in to comment.