diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/gitbook-core/src/bootstrap.js | 28 | ||||
-rw-r--r-- | packages/gitbook-core/src/components/Import.js | 37 | ||||
-rw-r--r-- | packages/gitbook-core/src/components/InjectedComponent.js | 8 | ||||
-rw-r--r-- | packages/gitbook-core/src/components/UnsafeComponent.js | 99 | ||||
-rw-r--r-- | packages/gitbook-core/src/index.js | 8 | ||||
-rw-r--r-- | packages/gitbook-core/src/lib/getPayload.js | 19 | ||||
-rw-r--r-- | packages/gitbook-core/src/renderWithStore.js | 18 | ||||
-rw-r--r-- | packages/gitbook-plugin-theme-default/src/Toolbar.js | 15 | ||||
-rw-r--r-- | packages/gitbook-plugin-theme-default/src/index.js | 6 | ||||
-rw-r--r-- | packages/gitbook/src/browser/render.js | 34 | ||||
-rw-r--r-- | packages/gitbook/src/output/website/copyPluginAssets.js | 60 |
11 files changed, 198 insertions, 134 deletions
diff --git a/packages/gitbook-core/src/bootstrap.js b/packages/gitbook-core/src/bootstrap.js new file mode 100644 index 0000000..a5b457b --- /dev/null +++ b/packages/gitbook-core/src/bootstrap.js @@ -0,0 +1,28 @@ +const ReactDOM = require('react-dom'); + +const getPayload = require('./lib/getPayload'); +const createStore = require('./createStore'); +const renderWithStore = require('./renderWithStore'); + +/** + * Bootstrap GitBook on the browser (this function should not be called on the server side) + */ +function bootstrap() { + const initialState = getPayload(window.document); + const plugins = window.gitbookPlugins; + + const mountNode = document.getElementById('content'); + + // Create the redux store + const store = createStore(plugins, initialState); + + window.appStore = store; + + // Render with the store + const el = renderWithStore(store); + + ReactDOM.render(el, mountNode); +} + + +module.exports = bootstrap; diff --git a/packages/gitbook-core/src/components/Import.js b/packages/gitbook-core/src/components/Import.js new file mode 100644 index 0000000..057ef7a --- /dev/null +++ b/packages/gitbook-core/src/components/Import.js @@ -0,0 +1,37 @@ +const React = require('react'); +const Head = require('react-helmet'); +const ReactRedux = require('react-redux'); + +const ImportLink = ReactRedux.connect((state, {rel, href}) => { + href = href; // TODO: resolve using current page + + return { + link: [ + { + rel, + href + } + ] + }; +})(Head); + +const ImportScript = ReactRedux.connect((state, {type, src}) => { + src = src; // TODO: resolve using current page + + return { + script: [ + { + type, + src + } + ] + }; +})(Head); + +const ImportCSS = props => <ImportLink rel="stylesheet" {...props} />; + +module.exports = { + ImportLink, + ImportScript, + ImportCSS +}; diff --git a/packages/gitbook-core/src/components/InjectedComponent.js b/packages/gitbook-core/src/components/InjectedComponent.js index a4f81fe..cb50e02 100644 --- a/packages/gitbook-core/src/components/InjectedComponent.js +++ b/packages/gitbook-core/src/components/InjectedComponent.js @@ -2,7 +2,6 @@ const React = require('react'); const ReactRedux = require('react-redux'); const { List } = require('immutable'); -const UnsafeComponent = require('./UnsafeComponent'); const { findMatchingComponents } = require('../actions/components'); /* @@ -37,11 +36,8 @@ const Injection = React.createClass({ const Comp = this.props.component; const { props, children } = this.props; - if (Comp.sandbox === false) { - return <Comp {...props}>{children}</Comp>; - } else { - return <UnsafeComponent Component={Comp} props={props}>{children}</UnsafeComponent>; - } + // TODO: try to render with an error handling for unsafe component + return <Comp {...props}>{children}</Comp>; } }); diff --git a/packages/gitbook-core/src/components/UnsafeComponent.js b/packages/gitbook-core/src/components/UnsafeComponent.js deleted file mode 100644 index 3534f45..0000000 --- a/packages/gitbook-core/src/components/UnsafeComponent.js +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable no-console */ -const React = require('react'); -const ReactDOM = require('react-dom'); -const ReactRedux = require('react-redux'); - -const isServerSide = typeof window === 'undefined'; - -/* - Public: Renders a component provided via the `component` prop, and ensures that - failures in the component's code do not cause state inconsistencies elsewhere in - the application. This component is used by {InjectedComponent} and - {InjectedComponentSet} to isolate third party code that could be buggy. - - Occasionally, having your component wrapped in {UnsafeComponent} can cause style - issues. For example, in a Flexbox, the `div.unsafe-component-wrapper` will cause - your `flex` and `order` values to be one level too deep. For these scenarios, - UnsafeComponent looks for `containerStyles` on your React component and attaches - them to the wrapper div. - */ - - -const UnsafeComponent = React.createClass({ - propTypes: { - Component: React.PropTypes.func.isRequired, - props: React.PropTypes.object, - children: React.PropTypes.node - }, - contextTypes: { - store: React.PropTypes.object - }, - - componentDidMount() { - return this.renderInjected(); - }, - - componentDidUpdate() { - return this.renderInjected(); - }, - - componentWillUnmount() { - return this.unmountInjected(); - }, - - getInjected() { - const { Component, props, children } = this.props; - const { store } = this.context; - - return ( - <ReactRedux.Provider store={store}> - <Component {...props}>{children}</Component> - </ReactRedux.Provider> - ); - }, - - renderInjected() { - const node = ReactDOM.findDOMNode(this); - - try { - this.injected = this.getInjected(); - - ReactDOM.render(this.injected, node); - } catch (err) { - console.error(err); - } - }, - - unmountInjected() { - try { - const node = ReactDOM.findDOMNode(this); - return ReactDOM.unmountComponentAtNode(node); - } catch (err) { - console.error(err); - } - }, - - focus() { - if (this.injected.focus != null) { - return this.injected.focus(); - } - }, - - blur() { - if (this.injected.blur != null) { - return this.injected.blur(); - } - }, - - render() { - let inner; - - if (isServerSide) { - inner = this.getInjected(); - } - - return <div name="unsafe-component-wrapper">{inner}</div>; - } -}); - -module.exports = ReactRedux.connect()(UnsafeComponent); diff --git a/packages/gitbook-core/src/index.js b/packages/gitbook-core/src/index.js index b4b2abd..46892e4 100644 --- a/packages/gitbook-core/src/index.js +++ b/packages/gitbook-core/src/index.js @@ -2,6 +2,7 @@ const Head = require('react-helmet'); const { Provider } = require('react-redux'); const { InjectedComponent, InjectedComponentSet } = require('./components/InjectedComponent'); +const { ImportLink, ImportScript, ImportCSS } = require('./components/Import'); const HTMLContent = require('./components/HTMLContent'); const { registerComponent } = require('./actions/components'); @@ -12,9 +13,13 @@ const connect = require('./connect'); const createPlugin = require('./createPlugin'); const createReducer = require('./createReducer'); const createStore = require('./createStore'); +const bootstrap = require('./bootstrap'); +const renderWithStore = require('./renderWithStore'); module.exports = { ACTIONS, + bootstrap, + renderWithStore, connect, createPlugin, createReducer, @@ -26,6 +31,9 @@ module.exports = { HTMLContent, Head, Provider, + ImportLink, + ImportScript, + ImportCSS, // Utilities Shapes }; diff --git a/packages/gitbook-core/src/lib/getPayload.js b/packages/gitbook-core/src/lib/getPayload.js new file mode 100644 index 0000000..2d54b9e --- /dev/null +++ b/packages/gitbook-core/src/lib/getPayload.js @@ -0,0 +1,19 @@ + +/** + * Get the payload for a GitBook page + * @param {String|DOMDocument} html + * @return {Object} + */ +function getPayload(html) { + if (typeof html === 'string') { + const parser = new DOMParser(); + html = parser.parseFromString(html, 'text/html'); + } + + const script = html.querySelector('script[type="application/payload+json"]'); + const payload = JSON.parse(script.innerHTML); + + return payload; +} + +module.exports = getPayload; diff --git a/packages/gitbook-core/src/renderWithStore.js b/packages/gitbook-core/src/renderWithStore.js new file mode 100644 index 0000000..af86704 --- /dev/null +++ b/packages/gitbook-core/src/renderWithStore.js @@ -0,0 +1,18 @@ +const React = require('react'); +const { Provider } = require('react-redux'); +const { InjectedComponent } = require('./components/InjectedComponent'); + +/** + * Render the application for a store + * @param {ReduxStore} store + * @return {React.Element} element + */ +function renderWithStore(store) { + return ( + <Provider store={store}> + <InjectedComponent matching={{ role: 'Body' }} /> + </Provider> + ); +} + +module.exports = renderWithStore; diff --git a/packages/gitbook-plugin-theme-default/src/Toolbar.js b/packages/gitbook-plugin-theme-default/src/Toolbar.js new file mode 100644 index 0000000..b3fd059 --- /dev/null +++ b/packages/gitbook-plugin-theme-default/src/Toolbar.js @@ -0,0 +1,15 @@ +const React = require('react'); +const GitBook = require('gitbook-core'); + +const Toolbar = React.createClass({ + render() { + return ( + <div className="Toolbar book-toolbar"> + <GitBook.InjectedComponentSet matching={{ role: 'toolbar:buttons:left' }} /> + <GitBook.InjectedComponentSet matching={{ role: 'toolbar:buttons:right' }} /> + </div> + ); + } +}); + +module.exports = Toolbar; diff --git a/packages/gitbook-plugin-theme-default/src/index.js b/packages/gitbook-plugin-theme-default/src/index.js index 6499d5a..4b98a22 100644 --- a/packages/gitbook-plugin-theme-default/src/index.js +++ b/packages/gitbook-plugin-theme-default/src/index.js @@ -3,6 +3,7 @@ const GitBook = require('gitbook-core'); const Sidebar = require('./Sidebar'); const Page = require('./Page'); +const Toolbar = require('./Toolbar'); let ThemeBody = React.createClass({ propTypes: { @@ -17,9 +18,10 @@ let ThemeBody = React.createClass({ <div className="GitBook book"> <GitBook.Head title={page.title} - titleTemplate="%s - GitBook" - /> + titleTemplate="%s - GitBook" /> + <GitBook.ImportCSS href="gitbook/gitbook-plugin-theme-default/theme.css" /> + <Toolbar /> <Sidebar /> <Page page={page} /> {children} diff --git a/packages/gitbook/src/browser/render.js b/packages/gitbook/src/browser/render.js index ee4600a..3696eba 100644 --- a/packages/gitbook/src/browser/render.js +++ b/packages/gitbook/src/browser/render.js @@ -4,7 +4,9 @@ const GitBook = require('gitbook-core'); const loadPlugins = require('./loadPlugins'); -function HTML({head, innerHTML}) { +const BOOTSTRAP_CODE = '(function() { require("gitbook-core").bootstrap() })()'; + +function HTML({head, innerHTML, payload, scripts}) { const attrs = head.htmlAttributes.toComponent(); return ( @@ -17,6 +19,11 @@ function HTML({head, innerHTML}) { </head> <body> <div id="content" dangerouslySetInnerHTML={{__html: innerHTML}} /> + {scripts.map(script => { + return <script key={script} src={script} />; + })} + <script type="application/payload+json" dangerouslySetInnerHTML={{__html: JSON.stringify(payload)}} /> + <script type="application/javascript" dangerouslySetInnerHTML={{__html: BOOTSTRAP_CODE}} /> {head.script.toComponent()} </body> </html> @@ -24,7 +31,9 @@ function HTML({head, innerHTML}) { } HTML.propTypes = { head: React.PropTypes.object, - innerHTML: React.PropTypes.string + innerHTML: React.PropTypes.string, + payload: React.PropTypes.object, + scripts: React.PropTypes.arrayOf(React.PropTypes.string) }; /** @@ -40,25 +49,10 @@ function render(plugins, initialState) { const scripts = plugins.toList() .filter(plugin => plugin.getPackage().has('browser')) - .map(plugin => { - return { src: '/gitbook/plugins/' + plugin.getName() + '.js' }; - }) + .map(plugin => 'gitbook/plugins/' + plugin.getName() + '.js') .toArray(); - scripts.push({ - type: 'application/payload+json', - innerHTML: JSON.stringify(initialState) - }); - - const el = ( - <GitBook.Provider store={store}> - <GitBook.InjectedComponent matching={{ role: 'Body' }}> - <GitBook.Head - script={scripts} - /> - </GitBook.InjectedComponent> - </GitBook.Provider> - ); + const el = GitBook.renderWithStore(store); // Render inner body const innerHTML = ReactDOMServer.renderToString(el); @@ -70,6 +64,8 @@ function render(plugins, initialState) { const htmlEl = <HTML head={head} innerHTML={innerHTML} + payload={initialState} + scripts={['gitbook/core.js'].concat(scripts)} />; const html = ReactDOMServer.renderToStaticMarkup(htmlEl); diff --git a/packages/gitbook/src/output/website/copyPluginAssets.js b/packages/gitbook/src/output/website/copyPluginAssets.js index 312d3d6..1291a06 100644 --- a/packages/gitbook/src/output/website/copyPluginAssets.js +++ b/packages/gitbook/src/output/website/copyPluginAssets.js @@ -6,8 +6,7 @@ const fs = require('../../utils/fs'); /** * Copy all assets from plugins. - * Assets are files stored in "_assets" - * and resources declared in the plugin itself. + * Assets are files stored in a "_assets" of the plugin. * * @param {Output} * @return {Promise} @@ -23,16 +22,16 @@ function copyPluginAssets(output) { const plugins = output.getPlugins() - // We reverse the order of plugins to copy - // so that first plugins can replace assets from other plugins. - .reverse(); + // We reverse the order of plugins to copy + // so that first plugins can replace assets from other plugins. + .reverse(); return Promise.forEach(plugins, function(plugin) { return copyAssets(output, plugin) - .then(function() { - return copyResources(output, plugin); - }); + .then(() => copyResources(output, plugin)) + .then(() => copyBrowserJS(output, plugin)); }) + .then(() => copyCoreJS(output)) .thenResolve(output); } @@ -70,6 +69,51 @@ function copyAssets(output, plugin) { } /** + * Copy JS file for the plugin + * + * @param {Plugin} + * @return {Promise} + */ +function copyBrowserJS(output, plugin) { + const logger = output.getLogger(); + const pluginRoot = plugin.getPath(); + const options = output.getOptions(); + const outputRoot = options.get('root'); + + let browserFile = plugin.getPackage().get('browser'); + + if (!browserFile) { + return Promise(); + } + + browserFile = path.join(pluginRoot, browserFile); + const outputFile = path.join(outputRoot, 'gitbook/plugins', plugin.getName() + '.js'); + + logger.debug.ln('copy browser JS file from plugin', browserFile); + return fs.ensureFile(outputFile) + .then(() => fs.copy(browserFile, outputFile)); +} + +/** + * Copy JS file for gitbook-core + * + * @param {Plugin} + * @return {Promise} + */ +function copyCoreJS(output) { + const logger = output.getLogger(); + const options = output.getOptions(); + const outputRoot = options.get('root'); + + const inputFile = require.resolve('gitbook-core/gitbook.core.min.js'); + const outputFile = path.join(outputRoot, 'gitbook/core.js'); + + logger.debug.ln('copy JS for gitbook-core'); + return fs.ensureFile(outputFile) + .then(() => fs.copy(inputFile, outputFile)); +} + +/** * Copy resources from a plugin * * @param {Plugin} |