markdalgleish / static-site-generator-webpack-plugin

Minimal, unopinionated static site generator powered by webpack
MIT License
1.61k stars 97 forks source link

Are you able to provide an example ./template.ejs file? #11

Closed That-David-Guy closed 8 years ago

That-David-Guy commented 9 years ago

I'm not sure what ./template.ejs would look like in your example. Are you able to provide an example or a link to an example please?

VaclavSynacek commented 9 years ago

Not sure about the ejs. But I managed to get it working without the use of ejs - I used react as templating engine. This needs 2 react renders on the server (one for the outer html template without the react handles and one for the app itself) and just one standard rendering on the client (for the app itself).

//package.json snippet
   { "dependencies": {
        "react": "^0.13.3", //sorry, I have not upgraded yet
        "react-helmet": "^1.1.5", //optional, just to get the title generated in static site for SEO
        "react-router": "^1.0.0-rc1"}}
//index.js
var React = require('react');
var Router = require('react-router/lib/Router');
var Routes = require('./Routes');

//client code
if (typeof document !== 'undefined') {
    var createBrowserHistory = require('history/lib/createBrowserHistory');

    var history = createBrowserHistory();
    //render app and attache to <div id="content"> in the HTML document
    React.render(<Router history={history}>{Routes}</Router>, document.getElementById('content'));
}

//static site generator code
module.exports = function render(locals, callback) {
    var Helmet = require('react-helmet');
    var createMemoryHistory = require('history/lib/createMemoryHistory');
    var Html = require('./Html.js');

    var history = createMemoryHistory(locals.path);

    //render app to string to be used later
    var reactApp = {
        __html: React.renderToString(<Router history={history}>{Routes}</Router>)
    };
    var head = Helmet.rewind();

    //render the outer HTML and pass the already rendered app as props to be inserted to the same place as it would be on client.
    var html = React.renderToStaticMarkup(<Html title={head.title} reactApp={reactApp} />);
    callback(null, '<!DOCTYPE html>' + html);
}
//Html.js
//component runs only during static site generation, never runs on client
//inserts props.reactApp to the exact place where client side renedering does
var React = require('react');

var Html = React.createClass({

    render: function() {
        return (

<html lang="en">
<head>
    <title>{this.props.title}</title>
    <script type="text/javascript" src="https://fb.me/react-0.13.3.min.js" ></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
</head>

<body>
    <div id="content" dangerouslySetInnerHTML={this.props.reactApp} />
    <script type="text/javascript" src="/bundle.js" charset="utf-8"></script>
</body>

</html>
        );
    }
});

module.exports = Html;

IMHO better solution than using ejs, because you need to use/learn one tool less. I would love to see this as official example in this repo.

svenheden commented 8 years ago

I don't think you need either a .ejs template or a React component for the "base HTML". I usually do something like this to keep it simple...

In a file called base-html.js I would have:

export default `<!doctype html>
<html>
    <head>
        [...]
    </head>
    <body>
        <div id="root">%PLACEHOLDER%</div>
        <script src="script.js"></script>
    </body>
</html>
`;

And to compile the template for use in my static site render function I would just...

const html = require('./base-html').replace(
  /%PLACEHOLDER%/,
  ReactDOMServer.renderToString(<RoutingContext {...renderProps} />)
);

KISS :)

bensmithett commented 8 years ago

I've found ES6 template strings also work well as a default :wink:

export default function appTemplate (htmlRenderedByReact, title, assets) {
  return `
<!doctype html>
<html lang='en'>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>${title}</title>
  </head>
  <body>
    <div class='js-app-shell'>${htmlRenderedByReact}</div>
    <script src='${assets.index}'></script>
  </body>
</html>
`
}
svenheden commented 8 years ago

Even better :+1:

svenheden commented 8 years ago

Or why not with ES6 arrow functions as well... ;)

export default (htmlRenderedByReact, title, assets) => `
<!doctype html>
<html lang='en'>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>${title}</title>
  </head>
  <body>
    <div class='js-app-shell'>${htmlRenderedByReact}</div>
    <script src='${assets.index}'></script>
  </body>
</html>
`
That-David-Guy commented 8 years ago

Thanks for all the tips!

I don't have access to the repo anymore that caused me to ask this question, which makes it difficult for me to test these answers. So I'm not sure if I should close this issue or not. I might be able to do up a test project after christmas?

svenheden commented 8 years ago

If you are happy with the answers I guess you can close the issue :)

That-David-Guy commented 8 years ago

Will do

TerrellV commented 8 years ago

@jonathanp in the ReadMe and your example, where do the renderProps come from ? I keep getting undefined when I try and console.log them but I'm not sure where they originate from or their importance ....

VaclavSynacek commented 8 years ago

If you are just starting with static-site-generator-plugin consider getting inspiration from VaclavSynacek/react-isomorphic-static-site-generator-boilerplate. It does not follow the static-site-generator-plugin README exactly to a point (does not use ejs for example), but it is a working 5 page example, running right after git clone on local and it even has a live demo running in gh-pages.