Merge pull request #2157 from lbke/apollo2-finalization

Apollo2 finalization (backward compatibility)
This commit is contained in:
Eric Burel 2019-01-02 15:43:40 +01:00 committed by GitHub
commit b1c06f2a33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 275 additions and 188 deletions

4
package-lock.json generated
View file

@ -6073,7 +6073,7 @@
"process": "^0.11.9",
"punycode": "^1.4.1",
"querystring-es3": "^0.2.1",
"readable-stream": "git+https://github.com/meteor/readable-stream.git#2e9112d7d31a2af6e0682db0e18679b1e5fd4694",
"readable-stream": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12",
"stream-browserify": "^2.0.1",
"string_decoder": "^1.0.1",
"timers-browserify": "^1.4.2",
@ -6520,7 +6520,7 @@
"integrity": "sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew="
},
"readable-stream": {
"version": "git+https://github.com/meteor/readable-stream.git#2e9112d7d31a2af6e0682db0e18679b1e5fd4694",
"version": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12",
"from": "git+https://github.com/meteor/readable-stream.git",
"requires": {
"inherits": "~2.0.1",

View file

@ -1,26 +0,0 @@
/**
* Client specific wrapper, such as the BrowserRouter
*/
import {
registerComponent,
} from 'meteor/vulcan:lib';
import React, { PureComponent } from 'react';
//import PropTypes from 'prop-types';
import { BrowserRouter } from 'react-router-dom'
class AppContainer extends PureComponent {
render() {
const { children } = this.props
return (
<BrowserRouter>
{children}
</BrowserRouter>
)
}
}
AppContainer.displayName = 'AppContainer';
registerComponent({ name: 'AppContainer', component: AppContainer })
export default AppContainer;

View file

@ -0,0 +1,30 @@
/**
* The App + relevant wrappers
*/
import React from 'react';
import { ApolloProvider } from 'react-apollo';
import { runCallbacks } from '../../modules'
import { Components } from 'meteor/vulcan:lib'
import { CookiesProvider } from 'react-cookie';
import { BrowserRouter } from 'react-router-dom'
const AppGenerator = ({ apolloClient }) => {
const App = (
<ApolloProvider client={apolloClient}>
<CookiesProvider>
<BrowserRouter>
<Components.App />
</BrowserRouter>
</CookiesProvider>
</ApolloProvider>
);
// run user registered callbacks to wrap the app
const WrappedApp = runCallbacks({
name: 'router.client.wrapper',
iterator: App,
properties: { apolloClient }
});
return WrappedApp;
};
export default AppGenerator;

View file

@ -1,4 +1,3 @@
import './components/AppContainer'
export * from '../modules/index.js';
export * from './start.jsx';

View file

@ -1,12 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { onPageLoad } from 'meteor/server-render';
import { ApolloProvider } from 'react-apollo';
import { CookiesProvider } from 'react-cookie';
import AppGenerator from './components/AppGenerator'
import {
createApolloClient,
Components,
populateComponentsApp,
populateRoutesApp,
initializeFragments
@ -25,11 +23,7 @@ Meteor.startup(() => {
document.body.appendChild(rootElement);
const Main = () => (
<ApolloProvider client={apolloClient}>
<CookiesProvider>
<Components.App />
</CookiesProvider>
</ApolloProvider>
<AppGenerator apolloClient={apolloClient} />
);
onPageLoad(() => {

View file

@ -107,7 +107,6 @@ class App extends PureComponent {
render() {
const routeNames = Object.keys(Routes);
return (
<Components.AppContainer>
<IntlProvider locale={this.getLocale()} key={this.getLocale()} messages={Strings[this.getLocale()]}>
<div className={`locale-${this.getLocale()}`}>
<Components.HeadTags />
@ -129,7 +128,6 @@ class App extends PureComponent {
)}
</div>
</IntlProvider>
</Components.AppContainer>
);
}
}

View file

@ -1,20 +0,0 @@
import {
registerComponent,
} from 'meteor/vulcan:lib';
import React, { PureComponent } from 'react';
//import PropTypes from 'prop-types';
class AppContainer extends PureComponent {
render() {
const { children } = this.props
return (
children
)
}
}
AppContainer.displayName = 'AppContainer';
registerComponent({ name: 'AppContainer', component: AppContainer })
export default AppContainer;

View file

@ -1,4 +1,3 @@
import './components/AppContainer';
import './start.js';
export * from '../modules/index.js';

View file

@ -1,7 +1,6 @@
import './auth.js';
export * from '../modules/index.js';
export * from './mongo_redux.js';
export * from './render_context.js';
export * from './inject_data.js';

View file

@ -1,12 +0,0 @@
// import { getRenderContext } from './render_context.js';
// const { store } = getRenderContext;
// // use global store
// Mongo.Collection.prototype.findRedux = function (selector = {}, options = {}) {
// return this.findInStore(store, selector, options);
// }
// Mongo.Collection.prototype.findOneRedux = function (_idOrObject) {
// return this.findOneInStore(store, _idOrObject);
// }

View file

@ -7,7 +7,6 @@ import './deep_extend.js';
// import './intl_polyfill.js';
// import './graphql.js';
import './icons.js';
import './mongo_redux.js';
export * from './components.js';
export * from './collections.js';
@ -17,7 +16,6 @@ export * from './routes.js';
export * from './utils.js';
export * from './settings.js';
export * from './strings.js';
export * from './redux.js';
export * from './headtags.js';
export * from './fragments.js';
export * from './apollo-common'

View file

@ -1,79 +0,0 @@
// import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
// // create store, and implement reload function
// export const configureStore = (reducers, initialState = {}, middlewares) => {
// let getReducers;
// if (typeof reducers === 'function') {
// getReducers = reducers;
// reducers = getReducers();
// }
// reducers = typeof reducers === 'object' ? combineReducers(reducers) : reducers;
// middlewares = Array.isArray(middlewares) ? middlewares : [middlewares];
// const store = createStore(
// // reducers
// reducers,
// // initial state
// initialState,
// // middlewares
// compose(
// applyMiddleware(...middlewares),
// typeof window !== 'undefined' && window.devToolsExtension ? window.devToolsExtension() : f => f,
// ),
// );
// store.reload = function reload(options = {}) {
// if (typeof options.reducers === 'function') {
// getReducers = options.reducers;
// options.reducers = undefined;
// }
// if (!options.reducers && getReducers) {
// options.reducers = getReducers();
// }
// if (options.reducers) {
// reducers = typeof options.reducers === 'object' ? combineReducers(options.reducers) : options.reducers;
// }
// this.replaceReducer(reducers);
// return store;
// };
// return store;
// };
// // action
// // **Notes: client side, addAction to browser**
// // **Notes: server side, addAction to server share with every req**
// let actions = {};
// export const addAction = (addedAction) => {
// actions = { ...actions, ...addedAction };
// return actions;
// };
// export const getActions = () => actions;
// // reducers
// // **Notes: client side, addReducer to browser**
// // **Notes: server side, addReducer to server share with every req**
// let reducers = {};
// export const addReducer = (addedReducer) => {
// reducers = { ...reducers, ...addedReducer };
// return reducers;
// };
// export const getReducers = () => reducers;
// // middlewares
// // **Notes: client side, addMiddleware to browser**
// // **Notes: server side, addMiddleware to server share with every req**
// let middlewares = [];
// export const addMiddleware = (middlewareOrMiddlewareArray, options = {}) => {
// const addedMiddleware = Array.isArray(middlewareOrMiddlewareArray) ? middlewareOrMiddlewareArray : [middlewareOrMiddlewareArray];
// if (options.unshift) {
// middlewares = [...addedMiddleware, ...middlewares];
// } else {
// middlewares = [...middlewares, ...addedMiddleware];
// }
// return middlewares;
// };
// export const getMiddlewares = () => middlewares;

View file

@ -5,23 +5,20 @@ import React from 'react';
import { ApolloProvider } from 'react-apollo';
import { StaticRouter } from 'react-router';
// TODO:
// Problem: Components is only created on Startup
// so Components.App is not defined here
import { Components } from 'meteor/vulcan:lib'
import { CookiesProvider } from 'react-cookie';
import Cookies from 'universal-cookie';
import Cookies from 'universal-cookie';
// 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, apolloClient, context }) => {
// TODO: universalCookies should be defined here, but it isn't
// @see https://github.com/meteor/meteor-feature-requests/issues/174#issuecomment-441047495
const cookies = new Cookies(req.cookies) // req.universalCookies;
const App = (
<ApolloProvider client={client}>
<ApolloProvider client={apolloClient}>
<StaticRouter location={req.url} context={context}>
<CookiesProvider cookies={cookies}>
<Components.App />
@ -29,6 +26,6 @@ const AppGenerator = ({ req, client, context }) => {
</StaticRouter>
</ApolloProvider>
);
return App;
return App
};
export default AppGenerator;

View file

@ -8,6 +8,7 @@ import React from 'react';
import ReactDOM from 'react-dom/server';
import { renderToStringWithData } from 'react-apollo';
import { runCallbacks } from '../../modules/callbacks'
import { createClient } from './apolloClient';
import Head from './components/Head'
@ -22,7 +23,8 @@ const makePageRenderer = ({ computeContext }) => {
// this avoids caching server side
const client = await createClient({req, computeContext});
// TODO? do we need this?
// Used by callbacks to handle side effects
// E.g storing the stylesheet generated by styled-components
const context = {};
@ -30,15 +32,22 @@ const makePageRenderer = ({ computeContext }) => {
// middlewares at this point
// @see https://github.com/meteor/meteor-feature-requests/issues/174#issuecomment-441047495
const App = <AppGenerator req={req} apolloClient={client} context={context} />
// run user registered callbacks that wraps the React app
const WrappedApp = runCallbacks({
name: 'router.server.wrapper',
iterator: App,
properties: { req, context, apolloClient: client }
});
// equivalent to calling getDataFromTree and then renderToStringWithData
const content = await renderToStringWithData(
<AppGenerator req={req} client={client} context={context} />
)
const htmlContent = await renderToStringWithData(WrappedApp)
// 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)
const wrappedHtmlContent = `<div id="react-app">${htmlContent}</div>`
sink.appendToBody(wrappedHtmlContent)
// TODO: this sounds cleaner but where do we add the <div id="react-app"> ?
//sink.renderIntoElementById('react-app', content)
@ -52,6 +61,13 @@ const makePageRenderer = ({ computeContext }) => {
<ApolloState initialState={initialState} />
)
sink.appendToBody(serializedApolloState)
// post render callback
runCallbacks({
name: 'router.server.postRender',
iterator: sink,
properties: { context }
});
}
return renderPage
}

View file

@ -0,0 +1,4 @@
import setupRedux from './setupRedux';
setupRedux();
export * from '../modules/index';

View file

@ -0,0 +1,13 @@
import React from 'react'
import { Provider } from 'react-redux'
import { addCallback } from 'meteor/vulcan:core'
import { initStore } from '../modules/store'
const setupRedux = () => {
const store = initStore()
addCallback('router.client.wrapper',
function ReduxStoreProvider(app) {
return <Provider store={store}>{app}</Provider>
});
}
export default setupRedux

View file

@ -0,0 +1 @@
export * from './redux.js';

View file

@ -0,0 +1,96 @@
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import _isEmpty from 'lodash/isEmpty'
// TODO: now we should add some callback call to add the store to
// Apollo SSR + client side too
// create store, and implement reload function
export const configureStore = (reducers = getReducers, initialState = {}, middlewares = getMiddlewares) => {
let getReducers;
if (typeof reducers === 'function') {
getReducers = reducers;
reducers = getReducers();
}
if (typeof reducers === 'object') {
// allow to tolerate empty reducers
//@see https://github.com/reduxjs/redux/issues/968
reducers = !_isEmpty(reducers) ? combineReducers(reducers) : () => {}
}
let getMiddlewares;
if (typeof middlewares === 'function') {
getMiddlewares = middlewares;
middlewares = getMiddlewares();
}
middlewares = Array.isArray(middlewares) ? middlewares : [middlewares];
const store = createStore(
// reducers
reducers,
// initial state
initialState,
// middlewares
compose(
applyMiddleware(...middlewares),
typeof window !== 'undefined' && window.devToolsExtension ? window.devToolsExtension() : f => f,
),
);
store.reload = function reload(options = {}) {
if (typeof options.reducers === 'function') {
getReducers = options.reducers;
options.reducers = undefined;
}
if (!options.reducers && getReducers) {
options.reducers = getReducers();
}
if (options.reducers) {
reducers = typeof options.reducers === 'object' ? combineReducers(options.reducers) : options.reducers;
}
this.replaceReducer(reducers);
return store;
};
return store;
};
// action
// **Notes: client side, addAction to browser**
// **Notes: server side, addAction to server share with every req**
let actions = {};
export const addAction = (addedAction) => {
actions = { ...actions, ...addedAction };
return actions;
};
export const getActions = () => actions;
// reducers
// **Notes: client side, addReducer to browser**
// **Notes: server side, addReducer to server share with every req**
let reducers = {};
export const addReducer = (addedReducer) => {
reducers = { ...reducers, ...addedReducer };
return reducers;
};
export const getReducers = () => reducers;
// middlewares
// **Notes: client side, addMiddleware to browser**
// **Notes: server side, addMiddleware to server share with every req**
let middlewares = [];
export const addMiddleware = (middlewareOrMiddlewareArray, options = {}) => {
const addedMiddleware = Array.isArray(middlewareOrMiddlewareArray) ? middlewareOrMiddlewareArray : [middlewareOrMiddlewareArray];
if (options.unshift) {
middlewares = [...addedMiddleware, ...middlewares];
} else {
middlewares = [...middlewares, ...addedMiddleware];
}
return middlewares;
};
export const getMiddlewares = () => middlewares;
let store
export const initStore = () => {
if (!store) {
store = configureStore()
}
return store
}
export const getStore = () => store

View file

@ -0,0 +1,4 @@
import setupRedux from './setupRedux';
setupRedux();
export * from '../modules/index';

View file

@ -0,0 +1,13 @@
import React from 'react'
import { Provider } from 'react-redux'
import { addCallback } from 'meteor/vulcan:core'
import { initStore } from '../modules/redux'
const setupRedux = () => {
const store = initStore()
addCallback('router.server.wrapper',
function ReduxStoreProvider(app) {
return <Provider store={store}>{app}</Provider>
});
}
export default setupRedux

View file

@ -0,0 +1,18 @@
Package.describe({
name: 'vulcan:redux',
summary: 'Add Redux to Vulcan.',
version: '1.12.8',
git: 'https://github.com/VulcanJS/Vulcan.git'
});
Package.onUse(function(api) {
api.versionsFrom('1.6.1');
api.use([
'vulcan:core@1.12.8',
]);
api.mainModule('lib/server/main.js', 'server');
api.mainModule('lib/client/main.js', 'client');
});

View file

@ -0,0 +1 @@
export * from '../modules/index';

View file

@ -0,0 +1,4 @@
import setupStyledComponents from './setupStyledComponents';
setupStyledComponents();
export * from '../modules/index';

View file

@ -0,0 +1,23 @@
// Setup SSR
import { ServerStyleSheet } from 'styled-components'
import { addCallback } from 'meteor/vulcan:core';
const setupStyledComponents = () => {
addCallback('router.server.wrapper', function collectStyles(app, { context }) {
const stylesheet = new ServerStyleSheet();
// @see https://www.styled-components.com/docs/advanced/#example
const wrappedApp = stylesheet.collectStyles(app)
// store the stylesheet to reuse it later
context.stylesheet = stylesheet
return wrappedApp
})
addCallback('router.server.postRender', function appendStyleTags(sink, { context }) {
sink.appendToHead(context.stylesheet.getStyleTags())
return sink
})
}
export default setupStyledComponents

View file

@ -0,0 +1,17 @@
Package.describe({
name: 'vulcan:styled-components',
summary: 'Add Styled Components to Vulcan.',
version: '1.12.8',
git: 'https://github.com/VulcanJS/Vulcan.git'
});
Package.onUse(function(api) {
api.versionsFrom('1.6.1');
api.use([
'vulcan:core@1.12.8',
]);
api.mainModule('lib/server/main.js', 'server');
api.mainModule('lib/client/main.js', 'client');
});