Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add very basic hooks before and after compile, allow paths to be modified #457

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/with-afterjs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
logs
*.log
npm-debug.log*
.DS_Store

coverage
node_modules
build
public/static
.env.local
.env.development.local
.env.test.local
.env.production.local
45 changes: 45 additions & 0 deletions examples/with-afterjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Razzle Custom Webpack Configuration Example

## How to use
Download the example [or clone the whole project](https://github.com/jaredpalmer/razzle.git):

```bash
curl https://codeload.github.com/jaredpalmer/razzle/tar.gz/master | tar -xz --strip=2 razzle-master/examples/with-custom-webpack-config
cd with-custom-webpack-config
```

Install it and run:

```bash
yarn install
yarn start
```

## Idea behind the example
This example demonstrates how to use a `razzle.config.js` file to modify Razzle's
underlying webpack configuration. It modifies the name of the server's output file
in production (`razzle build`).

Note that this file is not transpiled, and so you must write it with vanilla
Node.js-compatible JavaScript.

```js
// razzle.config.js
'use strict';

module.exports = {
modify(config, { target, dev }, webpack) {
const appConfig = config; // stay immutable here

// Change the name of the server output file in production
if (target === 'node' && !dev) {
appConfig.output.filename = 'custom.js';
}

return appConfig;
},
};



```
84 changes: 84 additions & 0 deletions examples/with-afterjs/lib/_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import { Switch, Route, withRouter } from 'react-router-dom';
import loadInitialProps from './loadInitialProps';

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data,
previousLocation: null,
};
this.prefetcherCache = {};
}

// only runs clizzient
componentWillReceiveProps(nextProps, nextState) {
const navigated = nextProps.location !== this.props.location;
if (navigated) {
window.scrollTo(0, 0);
// save the location so we can render the old screen
this.setState({
previousLocation: this.props.location,
data: undefined, // unless you want to keep it
});
loadInitialProps(this.props.routes, nextProps.location.pathname, {
location: nextProps.location,
history: nextProps.history,
})
.then(data => {
this.setState({ previousLocation: null, data: data[0] });
})
.catch(e => {
// @todo we should more cleverly handle errors???
console.log(e);
});
}
}

prefetch = pathname => {
loadInitialProps(this.props.routes, pathname, {
history: this.props.history,
})
.then(data => {
this.prefetcherCache = { ...this.prefetcherCache, [pathname]: data[0] };
})
.catch(e => console.log(e));
};

updateData = data => {
this.setState({ data });
};

render() {
const { previousLocation, data } = this.state;
const { location, history, match } = this.props;
const initialData = this.prefetcherCache[location.pathname]
? this.prefetcherCache[location.pathname]
: data;

return (
<Switch>
{this.props.routes.map((r, i) => (
<Route
key={`route--${i}`}
path={r.path}
exact={r.exact}
location={previousLocation || location}
render={props => {
return React.createElement(r.component, {
...initialData,
history,
location: previousLocation || location,
match,
prefetch: this.prefetch,
});
}}
/>
))}
</Switch>
);
}
}

export default withRouter(App);
49 changes: 49 additions & 0 deletions examples/with-afterjs/lib/_document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';

class Document extends React.Component {
static getInitialProps({ assets, data, renderPage }) {
const page = renderPage();
return { assets, data, ...page };
}

render() {
const { helmet, assets, data } = this.props;
// get attributes from React Helmet
const htmlAttrs = helmet.htmlAttributes.toComponent();
const bodyAttrs = helmet.bodyAttributes.toComponent();

return (
<html {...htmlAttrs}>
<head>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta charSet="utf-8" />
<title>Welcome to the Afterparty</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
{helmet.title.toComponent()}
{helmet.meta.toComponent()}
{helmet.link.toComponent()}
{assets.client.css && (
<link rel="stylesheet" href={assets.client.css} />
)}
</head>
<body {...bodyAttrs}>
<div id="root">DO_NOT_DELETE_THIS_YOU_WILL_BREAK_YOUR_APP</div>
<script
type="text/javascript"
dangerouslySetInnerHTML={{
__html: ` window.__AFTER__ = ${JSON.stringify(data)}; `,
}}
/>
<script
type="text/javascript"
src={assets.client.js}
defer
crossOrigin="anonymous"
/>
</body>
</html>
);
}
}

export default Document;
22 changes: 22 additions & 0 deletions examples/with-afterjs/lib/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './_app';
import ensureReady from './ensureReady';
import { BrowserRouter } from 'react-router-dom';

import routes from './_routes';

const data = window.__AFTER__;

ensureReady(routes).then(() => {
ReactDOM.hydrate(
<BrowserRouter>
<App routes={routes} data={data} />
</BrowserRouter>,
document.getElementById('root')
);
});

if (module.hot) {
module.hot.accept();
}
16 changes: 16 additions & 0 deletions examples/with-afterjs/lib/ensureReady.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { matchPath } from 'react-router-dom';

/**
* This helps us to make sure all the async code is loaded before rendering.
*/
export default function ensureReady(routes, pathname) {
return Promise.all(
routes.map(route => {
const match = matchPath(pathname || window.location.pathname, route);
if (match && route.component.load) {
return route.component.load();
}
return undefined;
})
);
}
22 changes: 22 additions & 0 deletions examples/with-afterjs/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import http from 'http';
import app from './server';

const server = http.createServer(app);

const port = process.env.PORT || 3000;

let currentApp = app;
server.listen(port, err => {
if (err) {
console.log(err);
}
console.log(`> Started server on port ${port}`);
});

if (module.hot) {
module.hot.accept('./server', () => {
server.removeListener('request', currentApp);
server.on('request', app);
currentApp = app;
});
}
23 changes: 23 additions & 0 deletions examples/with-afterjs/lib/loadInitialProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { matchPath } from 'react-router-dom';

export default function loadInitialProps(routes, pathname, ctx) {
const promises = [];
routes.some(route => {
const match = matchPath(pathname, route);
if (match && route.component.getInitialProps) {
promises.push(
route.component.load
? route.component
.load() // load it as well
.then(() =>
route.component
.getInitialProps({ match, ...ctx })
.catch(() => {})
)
: route.component.getInitialProps({ match, ...ctx }).catch(() => {})
);
}
return !!match;
});
return Promise.all(promises);
}
57 changes: 57 additions & 0 deletions examples/with-afterjs/lib/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import App from './_app';
import routes from './_routes';
import Doc from './_document';
import loadInitialProps from './loadInitialProps';
import React from 'react';
import url from 'url';
import Helmet from 'react-helmet';
import ReactDOMServer from 'react-dom/server';
import express from 'express';
import { StaticRouter } from 'react-router-dom';

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

const server = express();

const modPageFn = Page => props => <Page {...props} />;

server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get('/*', async (req, res) => {
try {
const context = {};
const data = await loadInitialProps(routes, url.parse(req.url).pathname, {
req,
res,
});

const renderPage = (fn = modPageFn) => {
const html = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
{fn(App)({ routes, data: data[0] })}
</StaticRouter>
);
const helmet = Helmet.renderStatic();
return { html, helmet };
};

const { html, ...docProps } = await Doc.getInitialProps({
req,
res,
assets,
renderPage,
data: data[0],
});

const doc = ReactDOMServer.renderToStaticMarkup(<Doc {...docProps} />);
res.send(
`<!doctype html>` +
doc.replace('DO_NOT_DELETE_THIS_YOU_WILL_BREAK_YOUR_APP', html)
);
} catch (error) {
res.json(error);
}
});

export default server;
24 changes: 24 additions & 0 deletions examples/with-afterjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "razzle-examples-with-custom-webpack-config",
"version": "0.8.7",
"license": "MIT",
"scripts": {
"start": "razzle start",
"build": "razzle build",
"test": "razzle test --env=jsdom",
"start:prod": "NODE_ENV=production node build/custom.js"
},
"dependencies": {
"chokidar": "^2.0.0",
"express": "^4.16.2",
"fs-extra": "^5.0.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-helmet": "^5.2.0",
"react-router-dom": "^4.2.2",
"serialize-javascript": "^1.3.0"
},
"devDependencies": {
"razzle": "^0.8.7"
}
}
Binary file added examples/with-afterjs/public/favicon.ico
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/with-afterjs/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *

Loading