├── .babelrc
├── .eslintrc
├── .gitignore
├── LICENSE
├── README.md
├── defaults
├── .eslintrc
├── _lib
│ ├── client.js
│ └── pages.js
├── config.json
├── index.js
├── nodemon.json
├── package.json
└── src
│ ├── browser.js
│ └── components
│ ├── Example.react.js
│ ├── Index.react.js
│ ├── NestedExample.react.js
│ ├── Root.react.js
│ └── routes.js
├── index.js
├── lib
└── react-static.js
└── package.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0,
3 | "optional": [
4 | "es7.asyncFunctions",
5 | "es7.exportExtensions"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "rules": {
4 | "strict": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
28 | node_modules
29 |
30 | defaults/_site
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Robert Pearce
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-static
2 | (_NO LONGER ON NPM & NOT MAINTAINED_)
3 | React static site generator framework for Node.js.
4 |
5 | ## What is this?
6 | This project exists as a static site generator that utilizes React components for markup but, unlike other static site generators, also generates the client-side React JavaScript to allow the linking between pages to be incredibly fast out of the box. This means you'll also be able to have any other fancy client-side-oriented React bundled, as well.
7 |
8 | Under the hood, this tool builds off of [React](https://github.com/facebook/react), [react-router](https://github.com/rackt/react-router) and [nodejs](https://github.com/nodejs/node) to build static markup and JavaScript.
9 |
10 | ## Installation
11 | If for some reason you'd like to install this:
12 |
13 | 1. clone this repository
14 | 1. run `$ npm install`
15 | 1. run `$ npm link`
16 | 1. from your projects folder, run `$ npm link react-static`
17 | 1. use the tool as specified below
18 |
19 | ## Usage
20 | Create a new `react-static` project:
21 |
22 | ```
23 | $ react-static new portfolio/
24 | Installing react-static in to `portfolio/`
25 | => Successfully installed in to `portfolio/`
26 | => Run the following to complete setup:
27 |
28 | $ cd portfolio/ && npm install
29 |
30 | => Once setup is complete, to run the development server:
31 |
32 | $ react-static serve
33 | ```
34 |
35 | Change directory in to `portfolio/` and install dependencies:
36 |
37 | ```
38 | $ cd portfolio/ && npm install
39 | ```
40 |
41 | This might take a minute. Once your dependencies are installed, start the local dev bundling and watching:
42 |
43 | ```
44 | $ react-static serve
45 |
46 | > @ build /Users/rpearce/Desktop/portfolio
47 | > npm run lint && babel-node --optional es7.asyncFunctions --stage 0 "index.js"
48 |
49 | > @ lint /Users/rpearce/Desktop/portfolio
50 | > eslint src
51 |
52 | => Building static assets...
53 | => Static assets written to _site/
54 | => A development server is running at http://localhost:4000
55 | ```
56 |
57 | Navigate to [http://localhost:4000](http://localhost:4000) and see the dummy components in action.
58 |
59 | From this point on, all you need to do with regards to this core functionality is make changes to your app, and `react-static serve` will re-bundle and re-serve everything automatically.
60 |
61 | _NOTE: Do not edit anything in the `\_site/` folder. This is regularly removed and recreated._
62 |
63 | ### Creating a Component
64 | Inside of the `src/components/` components directory is where your components live, and you are free to structure them in any way you see fit, but if you change the location of `routes.js`, you're going to have problems.
65 |
66 | Create a new file, e.g., `src/components/About.react.js`, and place this content inside of it:
67 |
68 | ```js
69 | import React from 'react';
70 |
71 | const About = () =>
72 |
73 |
74 |
About
75 |
76 |
77 |
78 | About.meta = {
79 | title: 'About',
80 | description: 'This is the about us page'
81 | };
82 |
83 | export default About;
84 | ```
85 |
86 | If you're wondering where `class` and `extend` are, don't worry! This is a stateless functional component, as included in the [React 0.14 release notes](NEED LINK), that also sets a few static properties: `title` and `description` that are used in `src/components/Root.react.js` to provide metadata for your page (extend this as you wish).
87 |
88 | Next go to your `src/components/routes.js` file and import your `About` component:
89 |
90 | ```js
91 | import About from './About.react';
92 | ```
93 |
94 | and add the route information to the `routes` object:
95 |
96 | ```js
97 | const routes = {
98 | // other routes
99 |
100 | About: {
101 | path: 'desired/path/to/about.html',
102 | component: About
103 | }
104 | }
105 | ```
106 |
107 | Once you've done this, if you haven't already run
108 |
109 | ```
110 | $ react-static serve
111 | ```
112 |
113 | go ahead and do that. Happy coding!
114 |
115 | ## Contribute
116 |
117 | 1. Check out the [issues](https://github.com/rpearce/react-static/issues)
118 | 1. Fork this repository
119 | 1. Clone your fork
120 | 1. Check out a feature branch (`$ git checkout -b my-feature`)
121 | 1. Make your changes and push your branch to your GitHub repo
122 | 1. Create a pull request from your branch to this repo's master
123 | 1. When all is merged, pull down the upstream changes to your master
124 |
--------------------------------------------------------------------------------
/defaults/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "rules": {
4 | "strict": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/defaults/_lib/client.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import browserify from 'browserify';
3 | import babelify from 'babelify';
4 | import source from 'vinyl-source-stream';
5 |
6 | const client = async () => {
7 | return browserify({ entries: 'src/browser.js', debug: true })
8 | .transform(babelify)
9 | .bundle()
10 | .pipe(source('app.js'))
11 | .pipe(gulp.dest('_site'));
12 | }
13 |
14 | export default client;
15 |
--------------------------------------------------------------------------------
/defaults/_lib/pages.js:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import fsp from 'fs-promise';
3 | import React from 'react';
4 | import { renderToString } from 'react-dom/server';
5 | import { match, RoutingContext } from 'react-router';
6 | import routes from '../src/components/routes';
7 |
8 | const buildPages = async () => {
9 | const allRoutes = [].concat(routes.indexRoute || []).concat(routes.childRoutes || []);
10 | allRoutes.forEach(matchAndWrite);
11 | };
12 |
13 | const matchAndWrite = ({ path }) => {
14 | /*
15 | * Prepend route path with `/` to build `location`.
16 | * E.g., `blog.html` will be `/blog.html`.
17 | * Given IndexRoute has no path, it keeps a default of `/`.
18 | */
19 | const location = '/'.concat(path || '');
20 |
21 | /*
22 | * Trigger react-router's route matching
23 | */
24 | match({ routes, location }, async function handleMatch(err, redirectLocation, renderProps) {
25 | if (err) { throw(err); };
26 |
27 | try {
28 | /*
29 | * Render component markup, create output directories
30 | * and write markup to a file whose filename is
31 | * extracted from the route's path.
32 | * Provide fallback of `index.html` for IndexRoute.
33 | */
34 |
35 | const componentHTML = renderToString(),
36 | directory = determineDirectory(path),
37 | filePath = path || 'index.html';
38 |
39 | execSync(`mkdir -p _site/${directory}`, { stdio: [0,1,2] });
40 | fsp.writeFile(`_site/${filePath}`, `${componentHTML}`, 'utf8', (err) => { if (err) { throw err; } });
41 |
42 | } catch(e) {
43 | console.error(e);
44 | }
45 | });
46 | };
47 |
48 | const determineDirectory = (path = '/') => {
49 | const pathArr = path.split('/');
50 | return pathArr.slice(0, pathArr.length - 1).join('/');
51 | }
52 |
53 | export default buildPages;
54 |
--------------------------------------------------------------------------------
/defaults/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "clientJS": true,
3 | "port": 4000
4 | }
5 |
--------------------------------------------------------------------------------
/defaults/index.js:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import express from 'express';
3 | import { clientJS, port } from './config';
4 | import buildPages from './_lib/pages';
5 | import buildClientJS from './_lib/client';
6 |
7 | async () => {
8 | try {
9 | console.log('=> Building static assets...');
10 |
11 | /*
12 | * Remove and recreate _site build folder
13 | */
14 | execSync('rm -rf _site');
15 | execSync('mkdir _site');
16 |
17 | /*
18 | * Build the static pages.
19 | */
20 | await buildPages();
21 |
22 | console.log('=> Static assets written to _site/');
23 |
24 | /*
25 | * Create client JS file (app.js) in the build
26 | * path if `clientJS` config option is `true`
27 | */
28 | if (clientJS) {
29 | await buildClientJS();
30 | }
31 |
32 | /*
33 | * Start dev server
34 | */
35 | const app = express();
36 |
37 | app.use('/', express.static('_site'));
38 | app.listen(port);
39 |
40 | console.log(`=> A development server is running at http://localhost:${port}`);
41 | } catch (err) {
42 | console.error(err);
43 | }
44 | }();
45 |
--------------------------------------------------------------------------------
/defaults/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": true,
3 | "script": "index.js",
4 | "exec": "npm run build",
5 | "ignore": [
6 | "_site/*",
7 | "*.log"
8 | ],
9 | "ext": "js json"
10 | }
11 |
--------------------------------------------------------------------------------
/defaults/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "index.js",
3 | "scripts": {
4 | "lint": "eslint src",
5 | "dev": "nodemon",
6 | "build": "babel-node --optional es7.asyncFunctions --stage 0"
7 | },
8 | "devDependencies": {
9 | "babel": "^5.8.29",
10 | "babel-eslint": "^4.1.4",
11 | "babelify": "^6.4.0",
12 | "browserify": "^12.0.1",
13 | "eslint": "^1.9.0",
14 | "express": "^4.13.3",
15 | "fs-promise": "^0.3.1",
16 | "gulp": "^3.9.0",
17 | "nodemon": "^1.8.1",
18 | "uglify-js": "^2.5.0",
19 | "vinyl-source-stream": "^1.1.0"
20 | },
21 | "dependencies": {
22 | "history": "^1.12.6",
23 | "react": "^0.14.2",
24 | "react-dom": "^0.14.2",
25 | "react-router": "^1.0.0-rc3"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/defaults/src/browser.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import Router from 'react-router';
4 | import createBrowserHistory from 'history/lib/createBrowserHistory';
5 | import routes from './components/routes';
6 |
7 | render((
8 |
9 | { routes }
10 |
11 | ), document);
12 |
--------------------------------------------------------------------------------
/defaults/src/components/Example.react.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Example = () =>
4 |
5 |
6 |