From b1033571b425a6e6b110019240b0096d7c78953a Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Wed, 2 May 2018 00:18:02 -0400 Subject: [PATCH 1/6] chore(rebuild): preload entry page to avoid flash during retrieval This change delays the dynamic part of app from mounting until the first page's content has loaded. This prevents the static content from being stripped from the DOM until dynamic bundle is there to replace it. Once we migrate to `remark-react`, the elements won't even be re-mounted as React's diffing algorithm will be used. All other pages are still loaded using dynamic `import()`s. --- src/index.jsx | 43 +++++++++++++++++++++++--------- src/utilities/content-utils.js | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/utilities/content-utils.js diff --git a/src/index.jsx b/src/index.jsx index e835e82955cf..abdb860d91cd 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,8 +1,17 @@ +// 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 // Consider `react-g-analytics` package @@ -10,15 +19,27 @@ const render = process.NODE_ENV === 'production' ? ReactDOM.hydrate : ReactDOM.r // Client Side Rendering if ( window.document !== undefined ) { - render(( - - ( - import(`./content/${path}`) } /> - )} /> - - ), 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(( + + ( + { + if ( path === entryPath ) { + return entryModule.default || entryModule; + + } else return import(`./content/${path}`); + }} /> + )} /> + + ), document.getElementById('root')); + }); } diff --git a/src/utilities/content-utils.js b/src/utilities/content-utils.js new file mode 100644 index 000000000000..bf44c2b30eee --- /dev/null +++ b/src/utilities/content-utils.js @@ -0,0 +1,45 @@ +/** + * Walk the given tree of content + * + * @param {object} tree - ... + * @param {function} callback - ... + */ +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 - ... + * @return {array} - ... + */ +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 - ... + * @param {function} test - ... + * @return {object} - ... + */ +export const FindInContent = (tree, test) => { + let list = FlattenContent(tree); + + return list.find(test); +}; From 7b7f59b77c5b7165cdea910975dcde38d68134f8 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 5 May 2018 10:36:36 -0400 Subject: [PATCH 2/6] refactor: move sorting/filtering to top-level using new plugin options --- package.json | 2 +- src/components/Site/Site.jsx | 21 +-------------------- webpack.common.js | 12 +++++++++++- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 56ba793821cd..be2ecf9d3efa 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/Site/Site.jsx b/src/components/Site/Site.jsx index ea37bec2fe37..9e4d53cec625 100644 --- a/src/components/Site/Site.jsx +++ b/src/components/Site/Site.jsx @@ -20,27 +20,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 = { diff --git a/webpack.common.js b/webpack.common.js index 10e06cc6bd2a..a1bf3263581e 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -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: { From 1261b51c72e795af93c427692e1c11db2d6afd73 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 5 May 2018 10:37:05 -0400 Subject: [PATCH 3/6] chore: remove obsolete todo --- src/server.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/server.jsx b/src/server.jsx index 582cc85ba114..035baa9e8b1d 100644 --- a/src/server.jsx +++ b/src/server.jsx @@ -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 ``, 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; From a5bd68ed81d7efc4a2b43e3b6c2a67a0ed737e5f Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 5 May 2018 10:38:32 -0400 Subject: [PATCH 4/6] refactor(site): finish content-utils thus simplifying the `Site` component --- src/components/Site/Site.jsx | 44 +++++----------------------------- src/utilities/content-utils.js | 38 +++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/components/Site/Site.jsx b/src/components/Site/Site.jsx index 9e4d53cec625..290e5bf6509f 100644 --- a/src/components/Site/Site.jsx +++ b/src/components/Site/Site.jsx @@ -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'; @@ -31,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 (
@@ -70,7 +74,7 @@ class Site extends React.Component { render={ props => ( - { this._pages.map(page => ( + { pages.map(page => ( { - return array.reduce((flat, item) => { - return flat.concat( - Array.isArray(item.children) ? this._flatten(item.children) : item - ); - }, []); - } - /** * Strip any non-applicable properties * @@ -154,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); diff --git a/src/utilities/content-utils.js b/src/utilities/content-utils.js index bf44c2b30eee..f411010875cc 100644 --- a/src/utilities/content-utils.js +++ b/src/utilities/content-utils.js @@ -1,8 +1,8 @@ /** * Walk the given tree of content * - * @param {object} tree - ... - * @param {function} callback - ... + * @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); @@ -17,8 +17,8 @@ export const WalkContent = (tree, callback) => { /** * Deep flatten the given `tree`s child nodes * - * @param {object} tree - ... - * @return {array} - ... + * @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 ) { @@ -34,12 +34,36 @@ export const FlattenContent = tree => { /** * Find an item within the given `tree` * - * @param {object} tree - ... - * @param {function} test - ... - * @return {object} - ... + * @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 => { + return item.extension === '.md'; + }); +}; From 1d104bf03ffbb95d94e22541a683e2485481428e Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 5 May 2018 10:38:56 -0400 Subject: [PATCH 5/6] refactor(splash): rename variable for clarity --- src/components/Splash/Splash.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Splash/Splash.jsx b/src/components/Splash/Splash.jsx index fcdeefbba718..a3720b571c0c 100644 --- a/src/components/Splash/Splash.jsx +++ b/src/components/Splash/Splash.jsx @@ -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'; @@ -21,7 +21,7 @@ const Splash = () => (
From b7ce4810426009206bbe038d159aa45b92865296 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 6 May 2018 19:17:50 -0400 Subject: [PATCH 6/6] chore(rebuild): address minor feedback --- src/utilities/content-utils.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/utilities/content-utils.js b/src/utilities/content-utils.js index f411010875cc..2bae423d4d22 100644 --- a/src/utilities/content-utils.js +++ b/src/utilities/content-utils.js @@ -51,9 +51,7 @@ export const FindInContent = (tree, test) => { * @return {array} - Immediate children of the given `tree` that are directories */ export const ExtractSections = tree => { - return tree.children.filter(item => ( - item.type === 'directory' - )); + return tree.children.filter(item => item.type === 'directory'); }; /** @@ -63,7 +61,5 @@ export const ExtractSections = tree => { * @return {array} - All markdown descendants of the given `tree` */ export const ExtractPages = tree => { - return FlattenContent(tree).filter(item => { - return item.extension === '.md'; - }); + return FlattenContent(tree).filter(item => item.extension === '.md'); };