mirror of
https://github.com/vale981/Vulcan
synced 2025-03-06 10:01:40 -05:00
files cleanup
This commit is contained in:
parent
42ac3c37db
commit
c08df1a36f
11 changed files with 144 additions and 190 deletions
|
@ -26,8 +26,7 @@ import getPlaygroundConfig from './playground';
|
|||
|
||||
import { GraphQLSchema } from '../../modules/graphql.js';
|
||||
|
||||
// SSR
|
||||
import { ssrMiddleware } from '../apollo-ssr'
|
||||
import { enableSSR } from '../apollo-ssr'
|
||||
/**
|
||||
* Options: Apollo server usual options
|
||||
*
|
||||
|
@ -91,7 +90,7 @@ const createApolloServer = ({ options: givenOptions = {}, config: givenConfig =
|
|||
|
||||
|
||||
// ssr
|
||||
//WebApp.connectHandlers.use(ssrMiddleware)
|
||||
enableSSR()
|
||||
|
||||
/*
|
||||
* Alternative syntax with Express instead of Connect
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
// TODO: adapt to Vulcan
|
||||
// @see https://github.com/apollographql/GitHunt-React/blob/master/src/routes/Html.js
|
||||
/* eslint-disable react/no-danger */
|
||||
|
||||
import React from 'react';
|
||||
import { Helmet } from 'react-helmet'
|
||||
|
||||
const Head = () => {
|
||||
// Helmet.rewind() is deprecated in favour of renderStatic() for better readability
|
||||
//@see https://github.com/nfl/react-helmet/releases/tag/5.0.0
|
||||
const helmet = Helmet.renderStatic()
|
||||
return (
|
||||
<head>
|
||||
{helmet.title.toComponent()}
|
||||
{helmet.meta.toComponent()}
|
||||
{helmet.link.toComponent()}
|
||||
</head>
|
||||
)
|
||||
}
|
||||
// this render the page and add a script that will load the serialized data
|
||||
const Html = ({ content, state }) => {
|
||||
return (
|
||||
<html>
|
||||
<Head />
|
||||
<body>
|
||||
<div id="root" dangerouslySetInnerHTML={{ __html: content }} />
|
||||
<script dangerouslySetInnerHTML={{
|
||||
__html: `window.__APOLLO_STATE__=${JSON.stringify(state).replace(/</g, '\\u003c')};`,
|
||||
}} />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default Html;
|
||||
|
||||
// Previous implementation from router.js:
|
||||
// Some code could be useful eg to handle CSS
|
||||
|
||||
//function sendSSRHtml(options, req, res, next, renderProps) {
|
||||
// const { css, html, styledComponentCss } = generateSSRData(options, req, res, renderProps);
|
||||
//
|
||||
// req.dynamicHead = req.dynamicHead || '';
|
||||
// req.dynamicBody = req.dynamicBody || '';
|
||||
//
|
||||
// // css declared in the project
|
||||
// if (css) {
|
||||
// req.dynamicHead += `<style id="${options.styleCollectorId || 'css-style-collector-data'}">${css}</style>`;
|
||||
// }
|
||||
//
|
||||
// // css collected by styled-components
|
||||
// if (styledComponentCss) {
|
||||
// req.dynamicHead += styledComponentCss;
|
||||
// }
|
||||
//
|
||||
// let rootElementAttributes = '';
|
||||
// const attributes = options.rootElementAttributes instanceof Array ? options.rootElementAttributes : [];
|
||||
// if (attributes[0] instanceof Array) {
|
||||
// for (let i = 0; i < attributes.length; i++) {
|
||||
// rootElementAttributes += ` ${attributes[i][0]}="${attributes[i][1]}"`;
|
||||
// }
|
||||
// } else if (attributes.length > 0) {
|
||||
// rootElementAttributes = ` ${attributes[0]}="${attributes[1]}"`;
|
||||
// }
|
||||
//
|
||||
// req.dynamicBody += `<${options.rootElementType || 'div'} id="${options.rootElement || 'react-app'}"${rootElementAttributes}>${html || ''}</${options.rootElementType || 'div'}>`;
|
||||
//
|
||||
// if (typeof options.htmlHook === 'function') {
|
||||
// const { dynamicHead, dynamicBody } = options.htmlHook(req, res, req.dynamicHead, req.dynamicBody);
|
||||
// req.dynamicHead = dynamicHead;
|
||||
// req.dynamicBody = dynamicBody;
|
||||
// }
|
||||
//
|
||||
// next();
|
||||
//}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Component that serialize the Apollo client state
|
||||
*
|
||||
* The client can then deserialize it and avoid unecessary requests
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
const ApolloState = ({ initialState }) => (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.__APOLLO_STATE__ = ${JSON.stringify(initialState).replace(
|
||||
/</g,
|
||||
'\\u003c'
|
||||
)};`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export default ApolloState;
|
|
@ -1,3 +1,6 @@
|
|||
/**
|
||||
* The App + relevant wrappers
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { StaticRouter } from 'react-router';
|
||||
|
@ -7,11 +10,10 @@ import { StaticRouter } from 'react-router';
|
|||
// so Components.App is not defined here
|
||||
import { Components } from 'meteor/vulcan:lib'
|
||||
|
||||
// TODO: adapt to Vulcan
|
||||
// The client-side App will instead use <BrowserRouter>
|
||||
// see client-side vulcan:core/lib/client/start.jsx implementation
|
||||
// we do the same server side
|
||||
const appGenerator = ({ req, client, context }) => {
|
||||
const AppGenerator = ({ req, client, context }) => {
|
||||
const App = (
|
||||
<ApolloProvider client={client}>
|
||||
<StaticRouter location={req.url} context={context}>
|
||||
|
@ -21,4 +23,4 @@ const appGenerator = ({ req, client, context }) => {
|
|||
);
|
||||
return App;
|
||||
};
|
||||
export default appGenerator;
|
||||
export default AppGenerator;
|
|
@ -5,7 +5,6 @@
|
|||
* /!\ It must be recreated on every request
|
||||
*/
|
||||
|
||||
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
|
30
packages/vulcan-lib/lib/server/apollo-ssr/enableSSR.js
Normal file
30
packages/vulcan-lib/lib/server/apollo-ssr/enableSSR.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Actually enable SSR
|
||||
*/
|
||||
|
||||
import {
|
||||
populateComponentsApp,
|
||||
populateRoutesApp,
|
||||
initializeFragments
|
||||
} from 'meteor/vulcan:lib';
|
||||
// onPageLoad is mostly equivalent to an Express middleware
|
||||
// excepts it is tailored to handle Meteor server side rendering
|
||||
import { onPageLoad } from 'meteor/server-render'
|
||||
|
||||
import renderPage from './renderPage'
|
||||
|
||||
|
||||
const enableSSR = () => {
|
||||
Meteor.startup(() => {
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
initializeFragments();
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
// render the page
|
||||
onPageLoad(renderPage)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export default enableSSR
|
||||
|
|
@ -1,2 +1 @@
|
|||
import './start'
|
||||
export { default as ssrMiddleware } from './ssrMiddleware'
|
||||
export { default as enableSSR } from './enableSSR'
|
88
packages/vulcan-lib/lib/server/apollo-ssr/renderPage.js
Normal file
88
packages/vulcan-lib/lib/server/apollo-ssr/renderPage.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Render the page server side
|
||||
* @see https://github.com/szomolanyi/MeteorApolloStarter/blob/master/imports/startup/server/ssr.js
|
||||
* @see https://github.com/apollographql/GitHunt-React/blob/master/src/server.js
|
||||
* @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#renderToStringWithData
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/server';
|
||||
import { renderToStringWithData } from 'react-apollo';
|
||||
|
||||
import createClient from './createApolloClient';
|
||||
|
||||
import Head from './components/Head'
|
||||
import ApolloState from './components/ApolloState'
|
||||
import AppGenerator from './components/AppGenerator';
|
||||
|
||||
const renderPage = async sink => {
|
||||
const req = sink.request
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = createClient(req);
|
||||
|
||||
// TODO? do we need this?
|
||||
const context = {};
|
||||
|
||||
// equivalent to calling getDataFromTree and then renderToStringWithData
|
||||
const content = await renderToStringWithData(
|
||||
<AppGenerator req={req} client={client} context={context} />
|
||||
)
|
||||
|
||||
// TODO: there should be a cleaner way to set this wrapper
|
||||
// id must always match the client side start.jsx file
|
||||
const wrappedContent = `<div id="react-app">${content}</div>`
|
||||
sink.appendToBody(wrappedContent)
|
||||
// TODO: this sounds cleaner but where do we add the <div id="react-app"> ?
|
||||
//sink.renderIntoElementById('react-app', content)
|
||||
|
||||
// add headers using helmet
|
||||
const head = ReactDOM.renderToString(<Head />)
|
||||
sink.appendToHead(head)
|
||||
|
||||
// add Apollo state, the client will then parse the string
|
||||
const initialState = client.extract();
|
||||
const serializedApolloState = ReactDOM.renderToString(
|
||||
<ApolloState initialState={initialState} />
|
||||
)
|
||||
sink.appendToBody(serializedApolloState)
|
||||
}
|
||||
|
||||
export default renderPage
|
||||
// FIRST TRY WITH EXPRESS
|
||||
// However this won't work as Meteor is in charge of loading relevant JS script
|
||||
// This code would only render dead HTML
|
||||
// Could be useful for SEO though or if we switch away from Meteor
|
||||
//const ssrMiddleware = (req, res) => {
|
||||
// // according to the Apollo doc, client needs to be recreated on every request
|
||||
// // this avoids caching server side
|
||||
// const client = createClient(req);
|
||||
// // TODO adapt to Vulcan
|
||||
// const context = {};
|
||||
//
|
||||
// const App = appGenerator({ req, client, context })
|
||||
//
|
||||
// // Alternative that relies on Meteor server-render:
|
||||
// // @see https://github.com/szomolanyi/MeteorApolloStarter/blob/master/imports/startup/server/ssr.js
|
||||
//
|
||||
// // TODO: adapt to Vulcan
|
||||
// // @see https://github.com/apollographql/GitHunt-React/blob/master/src/server.js
|
||||
// // @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#renderToStringWithData
|
||||
// renderToStringWithData(App)
|
||||
// .then(content => {
|
||||
// const initialState = client.extract();
|
||||
// const html = <Html content={content} state={initialState} />;
|
||||
// //const html = <Html content={content} client={client} />;
|
||||
// res.statusCode = 200;
|
||||
// //res.status(200);
|
||||
// res.end(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`, 'utf8');
|
||||
// //res.end();
|
||||
// })
|
||||
// .catch(e => {
|
||||
// console.error('RENDERING ERROR:', e); // eslint-disable-line no-console
|
||||
// res.statusCode = 500;
|
||||
// res.end(`An error occurred. Stack trace:\n\n${e.stack}`);
|
||||
// });
|
||||
//
|
||||
// // TODO here we actually render with context (see render_context)
|
||||
//};
|
||||
// export default ssrMiddleware;
|
|
@ -1,94 +0,0 @@
|
|||
/**
|
||||
* Server-side Apollo client
|
||||
*
|
||||
*
|
||||
* @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#server-initialization
|
||||
*/
|
||||
|
||||
// This example uses React Router v4, although it should work
|
||||
// equally well with other routers that support SSR
|
||||
|
||||
import { renderToStringWithData } from 'react-apollo';
|
||||
|
||||
import createClient from './createClient';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/server';
|
||||
|
||||
// onPageLoad is mostly equivalent to an Express middleware
|
||||
// excepts it is tailored to handle Meteor server side rendering
|
||||
import { onPageLoad } from 'meteor/server-render'
|
||||
|
||||
import Html from './Html';
|
||||
import Head from './Head'
|
||||
import appGenerator from './appGenerator';
|
||||
|
||||
onPageLoad(async (sink) => {
|
||||
const req = sink.request
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = createClient(req);
|
||||
// TODO adapt to Vulcan
|
||||
const context = {};
|
||||
|
||||
const App = appGenerator({ req, client, context })
|
||||
|
||||
// Alternative that relies on Meteor server-render:
|
||||
// @see https://github.com/szomolanyi/MeteorApolloStarter/blob/master/imports/startup/server/ssr.js
|
||||
|
||||
// TODO: adapt to Vulcan
|
||||
// @see https://github.com/apollographql/GitHunt-React/blob/master/src/server.js
|
||||
// @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#renderToStringWithData
|
||||
// equivalent to calling getDataFromTree and then renderToStringWithData
|
||||
//sink.appendToBody(ReactDOM.renderToStaticMarkup(<div id='react-app'></div>))
|
||||
const content = await renderToStringWithData(App)
|
||||
console.log(content.slice(0,100))
|
||||
const wrappedContent = `<div id="react-app">${content}</div>`
|
||||
sink.appendToBody(wrappedContent)
|
||||
//sink.renderIntoElementById('react-app', 'HI')//content)
|
||||
// add headers
|
||||
const head = ReactDOM.renderToString(Head)
|
||||
sink.appendToHead(head)
|
||||
// add data
|
||||
const initialState = client.extract();
|
||||
sink.appendToBody(ReactDOM.renderToString(
|
||||
<script dangerouslySetInnerHTML={{
|
||||
__html: `window.__APOLLO_STATE__ = ${JSON.stringify(initialState).replace(/</g, '\\u003c')};`,
|
||||
}} />
|
||||
))
|
||||
})
|
||||
|
||||
const ssrMiddleware = (req, res) => {
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = createClient(req);
|
||||
// TODO adapt to Vulcan
|
||||
const context = {};
|
||||
|
||||
const App = appGenerator({ req, client, context })
|
||||
|
||||
// Alternative that relies on Meteor server-render:
|
||||
// @see https://github.com/szomolanyi/MeteorApolloStarter/blob/master/imports/startup/server/ssr.js
|
||||
|
||||
// TODO: adapt to Vulcan
|
||||
// @see https://github.com/apollographql/GitHunt-React/blob/master/src/server.js
|
||||
// @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#renderToStringWithData
|
||||
renderToStringWithData(App)
|
||||
.then(content => {
|
||||
const initialState = client.extract();
|
||||
const html = <Html content={content} state={initialState} />;
|
||||
//const html = <Html content={content} client={client} />;
|
||||
res.statusCode = 200;
|
||||
//res.status(200);
|
||||
res.end(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`, 'utf8');
|
||||
//res.end();
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('RENDERING ERROR:', e); // eslint-disable-line no-console
|
||||
res.statusCode = 500;
|
||||
res.end(`An error occurred. Stack trace:\n\n${e.stack}`);
|
||||
});
|
||||
|
||||
// TODO here we actually render with context (see render_context)
|
||||
};
|
||||
export default ssrMiddleware;
|
|
@ -1,12 +0,0 @@
|
|||
import {
|
||||
populateComponentsApp,
|
||||
populateRoutesApp,
|
||||
initializeFragments
|
||||
} from 'meteor/vulcan:lib';
|
||||
|
||||
Meteor.startup(() => {
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
initializeFragments();
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
});
|
Loading…
Add table
Reference in a new issue