12 | { title }
13 |
14 |
15 |
16 |
17 |
24 |
25 | { children }
26 | { script }
27 |
28 |
29 | );
30 | };
31 |
32 | export default Root;
33 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-static",
3 | "version": "0.1.1",
4 | "description": "React static site generator framework for Node.js",
5 | "main": "index.js",
6 | "scripts": {
7 | "clean": "rm -f index.js",
8 | "lint": "eslint src",
9 | "watch": "babel lib/react-static.js -w -o index.js",
10 | "build": "babel lib/react-static.js -o index.js",
11 | "prebuild": "npm run clean && npm run lint"
12 | },
13 | "keywords": [
14 | "react",
15 | "reactjs",
16 | "static",
17 | "static site",
18 | "site generator",
19 | "static site framework",
20 | "react static",
21 | "react static framework"
22 | ],
23 | "author": "Robert Pearce",
24 | "license": "MIT",
25 | "dependencies": {
26 | "babel": "^5.8.29",
27 | "commander": "^2.9.0"
28 | },
29 | "devDependencies": {
30 | "babel-eslint": "^4.1.4",
31 | "eslint": "^1.9.0"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/rpearce/react-static.git"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/rpearce/react-static/issues"
39 | },
40 | "homepage": "https://github.com/rpearce/react-static#readme",
41 | "preferGlobal": "true",
42 | "bin": {
43 | "react-static": "index.js"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/defaults/src/components/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Root from './Root.react';
3 | import Index from './Index.react';
4 | import Example from './Example.react';
5 | import NestedExample from './NestedExample.react';
6 |
7 | /*
8 | * Define your routes and their desired
9 | * output paths here.
10 | *
11 | * DO NOT prefix a `path` with a `/` (backslash)
12 | */
13 | const routes = {
14 | IndexRoute: {
15 | component: Index
16 | },
17 | ExampleRoute: {
18 | path: 'example.html',
19 | component: Example
20 | },
21 | NestedExampleRoute: {
22 | path: 'this/is/a/ridiculously/nested/example.html',
23 | component: NestedExample
24 | }
25 | };
26 |
27 | /*
28 | * Helper function so that we can
29 | * read all the route keys in `routes`
30 | * and export them to `childRoutes`
31 | * in our default export.
32 | */
33 | const getChildRoutes = () => {
34 | let childRoutes = [];
35 | for (let key in routes) {
36 | if (key !== 'IndexRoute') {
37 | childRoutes.push(routes[key]);
38 | }
39 | }
40 | return childRoutes;
41 | };
42 |
43 | export const path = (route) => {
44 | return '/'.concat(routes[route].path || '');
45 | }
46 |
47 | export default {
48 | path: '/',
49 | component: Root,
50 | indexRoute: routes.IndexRoute,
51 | childRoutes: getChildRoutes()
52 | };
53 |
--------------------------------------------------------------------------------
/lib/react-static.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import path from 'path';
4 | import { execSync } from 'child_process';
5 | import program from 'commander';
6 | import pkg from './package.json';
7 |
8 | const defaultsPath = path.join(__dirname, 'defaults/');
9 | const execCallback = (err) => { console.log(err); if (err !== null) { throw err; } };
10 |
11 | program
12 | .command('new [path]')
13 | .description('create a new react-static project')
14 | .action(function handleNew(path, options) {
15 | if (path) {
16 | console.log(`Installing react-static in to \`${path}\``);
17 | execSync(`mkdir -p ${path}`);
18 | execSync(`cp -r ${defaultsPath} ${path}`);
19 | console.log(`=> Successfully installed in to \`${path}\``);
20 | console.log(`=> Run the following to complete setup:\n\n $ cd ${path} && npm install\n`);
21 | console.log('=> Once setup is complete, to run the development server:\n\n $ react-static serve');
22 | } else {
23 | console.error('Error: Please provide a directory name to `react-static new `');
24 | }
25 | });
26 |
27 | program
28 | .command('serve')
29 | .description('run the development server and have it watch for changes')
30 | .action(function handleServe() {
31 | execSync('node_modules/.bin/nodemon', { stdio: [0,1,2] });
32 | });
33 |
34 | program
35 | .version(pkg.version)
36 | .parse(process.argv);
37 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 |
4 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
5 |
6 | var _path = require('path');
7 |
8 | var _path2 = _interopRequireDefault(_path);
9 |
10 | var _child_process = require('child_process');
11 |
12 | var _commander = require('commander');
13 |
14 | var _commander2 = _interopRequireDefault(_commander);
15 |
16 | var _packageJson = require('./package.json');
17 |
18 | var _packageJson2 = _interopRequireDefault(_packageJson);
19 |
20 | var defaultsPath = _path2['default'].join(__dirname, 'defaults/');
21 | var execCallback = function execCallback(err) {
22 | console.log(err);if (err !== null) {
23 | throw err;
24 | }
25 | };
26 |
27 | _commander2['default'].command('new [path]').description('create a new react-static project').action(function handleNew(path, options) {
28 | if (path) {
29 | console.log('Installing react-static in to `' + path + '`');
30 | (0, _child_process.execSync)('mkdir -p ' + path);
31 | (0, _child_process.execSync)('cp -r ' + defaultsPath + ' ' + path);
32 | console.log('=> Successfully installed in to `' + path + '`');
33 | console.log('=> Run the following to complete setup:\n\n $ cd ' + path + ' && npm install\n');
34 | console.log('=> Once setup is complete, to run the development server:\n\n $ react-static serve');
35 | } else {
36 | console.error('Error: Please provide a directory name to `react-static new `');
37 | }
38 | });
39 |
40 | _commander2['default'].command('serve').description('run the development server and have it watch for changes').action(function handleServe() {
41 | (0, _child_process.execSync)('node_modules/.bin/nodemon', { stdio: [0, 1, 2] });
42 | });
43 |
44 | _commander2['default'].version(_packageJson2['default'].version).parse(process.argv);
45 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------