Skip to content

chore(rebuild): properly utilize static html #2074

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

Merged
merged 7 commits into from
May 6, 2018
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"clean-webpack-plugin": "^0.1.17",
"copy-webpack-plugin": "^4.3.0",
"css-loader": "^0.28.5",
"directory-tree-webpack-plugin": "^0.2.0",
"directory-tree-webpack-plugin": "^0.3.1",
"duplexer": "^0.1.1",
"eslint": "4.5.0",
"eslint-loader": "^1.9.0",
Expand Down
36 changes: 22 additions & 14 deletions src/components/Page/Page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,28 @@ import Gitter from '../Gitter/Gitter';
import './Page.scss';

class Page extends React.Component {
state = {
content: this.props.content instanceof Promise ? 'Loading...' : this.props.content
constructor(props) {
super(props);

const { content } = props;

this.state = {
content: content instanceof Promise ? 'Loading...' : content.default || content
};
}

componentDidMount() {
const { content } = this.props;

if (content instanceof Promise) {
content
.then(module => this.setState({
content: module.default || module
}))
.catch(error => this.setState({
content: 'Error loading content.'
}));
}
}

render() {
Expand Down Expand Up @@ -66,18 +86,6 @@ class Page extends React.Component {
</section>
);
}

componentDidMount() {
if ( this.props.content instanceof Promise ) {
this.props.content
.then(module => this.setState({
content: module.default || module
}))
.catch(error => this.setState({
content: 'Error loading content.'
}));
}
}
}

export default Page;
65 changes: 7 additions & 58 deletions src/components/Site/Site.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { hot as Hot } from 'react-hot-loader';

// Import Utilities
import { ExtractPages, ExtractSections } from '../../utilities/content-utils';

// Import Components
import NotificationBar from '../NotificationBar/NotificationBar';
import Navigation from '../Navigation/Navigation';
Expand All @@ -20,27 +23,8 @@ import '../../styles/index';
import '../../styles/icon.font.js';
import './Site.scss';

// Load & Clean Up JSON Representation of `src/content`
// TODO: Consider moving all or parts of this cleaning to a `DirectoryTreePlugin` option(s)
// Load Content Tree
import Content from '../../_content.json';
Content.children = Content.children
.filter(item => item.name !== 'images')
.map(item => {
if ( item.type === 'directory' ) {
return {
...item,
children: item.children.sort((a, b) => {
let group1 = (a.group || '').toLowerCase();
let group2 = (b.group || '').toLowerCase();

if (group1 < group2) return -1;
if (group1 > group2) return 1;
return a.sort - b.sort;
})
};

} else return item;
});

class Site extends React.Component {
state = {
Expand All @@ -50,8 +34,9 @@ class Site extends React.Component {
render() {
let { location } = this.props;
let { mobileSidebarOpen } = this.state;
let sections = this._sections;
let sections = ExtractSections(Content);
let section = sections.find(({ url }) => location.pathname.startsWith(url));
let pages = ExtractPages(Content);

return (
<div className="site">
Expand Down Expand Up @@ -89,7 +74,7 @@ class Site extends React.Component {
render={ props => (
<Container className="site__content">
<Switch>
{ this._pages.map(page => (
{ pages.map(page => (
<Route
key={ page.url }
exact={ true }
Expand Down Expand Up @@ -142,20 +127,6 @@ class Site extends React.Component {
});
}

/**
* Flatten an array of `Content` items
*
* @param {array} array - ...
* @return {array} - ...
*/
_flatten = array => {
return array.reduce((flat, item) => {
return flat.concat(
Array.isArray(item.children) ? this._flatten(item.children) : item
);
}, []);
}

/**
* Strip any non-applicable properties
*
Expand All @@ -173,28 +144,6 @@ class Site extends React.Component {
children: children ? this._strip(children) : []
}));
}

/**
* Get top-level sections
*
* @return {array} - ...
*/
get _sections() {
return Content.children.filter(item => (
item.type === 'directory'
));
}

/**
* Get all markdown pages
*
* @return {array} - ...
*/
get _pages() {
return this._flatten(Content.children).filter(item => {
return item.extension === '.md';
});
}
}

export default Hot(module)(Site);
6 changes: 3 additions & 3 deletions src/components/Splash/Splash.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import SplashViz from '../SplashViz/SplashViz';
import Markdown from '../Markdown/Markdown';
import Support from '../Support/Support';

// Import Content
import Content from '../../content/index.md';
// Import Demo Content
import SplashContent from '../../content/index.md';

// Load Styling
import './Splash.scss';
Expand All @@ -21,7 +21,7 @@ const Splash = () => (
<Container>
<Markdown>
<div dangerouslySetInnerHTML={{
__html: Content
__html: SplashContent
}} />
</Markdown>
</Container>
Expand Down
45 changes: 34 additions & 11 deletions src/index.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
// Import External Dependencies
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';

// Import Components
import Site from './components/Site/Site';

// Import Utilities
import { FindInContent } from './utilities/content-utils';

// Import Content Tree
import Content from './_content.json';

// TODO: Re-integrate <GoogleAnalytics analyticsId="UA-46921629-2" />
// Consider `react-g-analytics` package

const render = process.NODE_ENV === 'production' ? ReactDOM.hydrate : ReactDOM.render;

// Client Side Rendering
if ( window.document !== undefined ) {
ReactDOM.render((
<BrowserRouter>
<Route
path="/"
render={ props => (
<Site
{ ...props }
import={ path => import(`./content/${path}`) } />
)} />
</BrowserRouter>
), document.getElementById('root'));
let { pathname } = window.location;
let trimmed = pathname.replace(/(.+)\/$/, '$1');
let entryPage = FindInContent(Content, item => item.url === trimmed);
let entryPath = entryPage.path.replace('src/content/', '');

import(`./content/${entryPath}`).then(entryModule => {
render((
<BrowserRouter>
<Route
path="/"
render={ props => (
<Site
{ ...props }
import={ path => {
if ( path === entryPath ) {
return entryModule.default || entryModule;

} else return import(`./content/${path}`);
}} />
)} />
</BrowserRouter>
), document.getElementById('root'));
});
}
6 changes: 1 addition & 5 deletions src/server.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ const bundles = [
'/index.bundle.js'
];

// Export method for `StaticSiteGeneratorPlugin`
// CONSIDER: How high can we mount `Site` into the DOM hierarchy? If
// we could start at `<html>`, much of this could be moved to the `Site`
// component itself (allowing easier utilization of page data for title,
// description, etc).
// Export method for `SSGPlugin`
export default locals => {
let { assets } = locals.webpackStats.compilation;

Expand Down
65 changes: 65 additions & 0 deletions src/utilities/content-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Walk the given tree of content
*
* @param {object} tree - Any node in the content tree
* @param {function} callback - Run on every descendant as well as the given `tree`
*/
export const WalkContent = (tree, callback) => {
callback(tree);

if ( tree.children ) {
tree.children.forEach(child => {
WalkContent(child, callback);
});
}
};

/**
* Deep flatten the given `tree`s child nodes
*
* @param {object} tree - Any node in the content tree
* @return {array} - A flattened list of leaf node descendants
*/
export const FlattenContent = tree => {
if ( tree.children ) {
return tree.children.reduce((flat, item) => {
return flat.concat(
Array.isArray(item.children) ? FlattenContent(item) : item
);
}, []);

} else return [];
};

/**
* Find an item within the given `tree`
*
* @param {object} tree - Any node in the content tree
* @param {function} test - A callback to find any leaf node in the given `tree`
* @return {object} - The first leaf node that passes the `test`
*/
export const FindInContent = (tree, test) => {
let list = FlattenContent(tree);

return list.find(test);
};

/**
* Get top-level sections
*
* @param {object} tree - Any node in the content tree
* @return {array} - Immediate children of the given `tree` that are directories
*/
export const ExtractSections = tree => {
return tree.children.filter(item => item.type === 'directory');
};

/**
* Get all markdown pages
*
* @param {object} tree - Any node in the content tree
* @return {array} - All markdown descendants of the given `tree`
*/
export const ExtractPages = tree => {
return FlattenContent(tree).filter(item => item.extension === '.md');
};
12 changes: 11 additions & 1 deletion webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,17 @@ module.exports = (env = {}) => ({
dir: 'src/content',
path: 'src/_content.json',
extensions: /\.md/,
enhance: treePluginEnhacer
enhance: treePluginEnhacer,
filter: item => item.name !== 'images',
sort: (a, b) => {
let group1 = (a.group || '').toLowerCase();
let group2 = (b.group || '').toLowerCase();

if (group1 < group2) return -1;
if (group1 > group2) return 1;
if (a.sort && b.sort) return a.sort - b.sort;
else return 0;
}
})
],
stats: {
Expand Down