diff --git a/bundle/.yarnclean b/bundle/.yarnclean new file mode 100644 index 00000000..e69de29b diff --git a/bundle/package.json b/bundle/package.json index 5ba9f892..59bb8eac 100644 --- a/bundle/package.json +++ b/bundle/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MPL-2.0", "scripts": { - "start": "NODE_ENV=development PORT=3069 REACT_APP_GQL_PORT=3069 REACT_APP_GQL_PROTOCOL=http node src/index.js", + "start": "UMD=1 NODE_ENV=development PORT=3069 REACT_APP_GQL_PORT=3069 REACT_APP_GQL_HOSTNAME=localhost REACT_APP_GQL_PROTOCOL=http node src/index.js", "lint-ci": "echo 0", "lint": "echo 0", "test-ci": "echo 0", @@ -12,12 +12,14 @@ "prepublish": "echo 0" }, "dependencies": { + "brok": "^2.0.0", "brule": "^3.1.0", "cloudapi-gql": "^4.6.0", "hapi": "^17.2.0", "hapi-triton-auth": "^1.0.0", "inert": "^5.1.0", "joyent-navigation": "*", + "my-images-console": "*", "my-joy-beta": "*", "rollover": "^1.0.0" } diff --git a/bundle/src/index.js b/bundle/src/index.js index 21e6639d..4bae3f0e 100644 --- a/bundle/src/index.js +++ b/bundle/src/index.js @@ -3,6 +3,7 @@ const Brule = require('brule'); const Hapi = require('hapi'); const Rollover = require('rollover'); +const Brok = require('brok'); const { homedir } = require('os'); const { join } = require('path'); @@ -10,7 +11,6 @@ process.env.SDC_KEY_PATH = process.env.SDC_KEY_PATH || join(homedir(), '.ssh/id_rsa'); const Sso = require('hapi-triton-auth'); -const Ui = require('my-joy-beta'); const Nav = require('joyent-navigation'); const Api = require('cloudapi-gql'); @@ -24,16 +24,28 @@ const { SDC_URL, BASE_URL = `http://0.0.0.0:${PORT}`, ROLLBAR_SERVER_TOKEN, - NODE_ENV = 'development' + NODE_ENV = 'development', + CONSOLE = 'my-joy-beta' } = process.env; +const Ui = require(CONSOLE); + const server = Hapi.server({ + compression: { + minBytes: 1 + }, + debug: { + request: ['error'] + }, port: PORT, host: '127.0.0.1' }); async function main() { await server.register([ + { + plugin: Brok + }, { plugin: Rollover, options: { diff --git a/packages/icons/package.json b/packages/icons/package.json index ecee41e7..2fdfb0d4 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -20,16 +20,16 @@ "dependencies": { "remcalc": "^1.0.10", "rnd-id": "^2.0.2", - "styled-components": "^3.1.4" + "styled-components": "^3.1.6" }, "devDependencies": { "babel-cli": "^6.26.0", - "babel-preset-joyent-portal": "^6.0.3", - "eslint": "^4.16.0", + "babel-preset-joyent-portal": "^7.0.1", + "eslint": "^4.18.1", "eslint-config-joyent-portal": "^3.3.1", "joyent-react-scripts": "^7.3.0", "react": "^16.2.0", - "redrun": "^5.10.0" + "redrun": "^5.10.5" }, "peerDependencies": { "react": "^16.2.0" diff --git a/packages/images/.babelrc b/packages/images/.babelrc index bc07a154..43a9ecd7 100644 --- a/packages/images/.babelrc +++ b/packages/images/.babelrc @@ -1,3 +1,7 @@ { - "presets": "joyent-portal" + "ignore": ["_document.js"], + "presets": [["joyent-portal", { + "aliases": true, + "autoAliases": true + }]] } diff --git a/packages/images/.eslintignore b/packages/images/.eslintignore index 0321eefc..27927eba 100644 --- a/packages/images/.eslintignore +++ b/packages/images/.eslintignore @@ -1,4 +1,5 @@ .nyc_output coverage dist -build \ No newline at end of file +build +lib/app \ No newline at end of file diff --git a/packages/images/.gitignore b/packages/images/.gitignore index 0bb3d99c..1f22d086 100644 --- a/packages/images/.gitignore +++ b/packages/images/.gitignore @@ -21,3 +21,5 @@ yarn-error.log* **/__diff_output__ + +lib/app \ No newline at end of file diff --git a/packages/images/lib/index.js b/packages/images/lib/index.js index 7c81bee0..ceaeea9b 100644 --- a/packages/images/lib/index.js +++ b/packages/images/lib/index.js @@ -1,22 +1,104 @@ const Inert = require('inert'); const Path = require('path'); -const Execa = require('execa'); -const { readFile } = require('mz/fs'); +const RenderReact = require('hapi-render-react'); +const Wreck = require('wreck'); +const Url = require('url'); exports.register = async server => { - await Execa('npm', ['run', 'build'], { - cwd: Path.join(__dirname, '..'), - stdio: 'inherit' - }); - - const indexFile = await readFile( - Path.join(__dirname, '../build/index.html'), - 'utf-8' - ); - - await server.register(Inert); + await server.register([ + { + plugin: Inert + }, + { + plugin: RenderReact, + options: { + relativeTo: Path.join(__dirname, 'app') + } + } + ]); server.route([ + { + method: 'GET', + path: '/service-worker.js', + config: { + auth: false, + handler: { + file: { + path: Path.join(__dirname, '../build/service-worker.js') + } + } + } + }, + { + method: 'GET', + path: '/favicon.ico', + config: { + auth: false, + handler: { + file: { + path: Path.join(__dirname, '../build/favicon.ico') + } + } + } + }, + { + method: 'GET', + path: '/font/{pathname*}', + config: { + auth: false, + handler: async (request, h) => { + const { params } = request; + const { pathname } = params; + + const location = Url.format({ + protocol: 'https:', + slashes: true, + host: 'fonts.gstatic.com', + pathname + }); + + const res = await Wreck.request('GET', location); + return h.response(res); + } + } + }, + { + method: 'GET', + path: '/fonts/css', + config: { + auth: false, + handler: async (request, h) => { + const { query, headers } = request; + const { family } = query; + const { host } = headers; + const url = Url.parse(`http://${host}`); + + const location = Url.format({ + protocol: 'https:', + slashes: true, + host: 'fonts.googleapis.com', + pathname: '/css', + query: { family } + }); + + const res = await Wreck.request('GET', location); + const body = await Wreck.read(res); + + const _body = body.toString().replace( + /https:\/\/fonts\.gstatic\.com/g, + `http://${url.host}/font` + ); + + return h + .response(_body) + .header('content-type', res.headers['content-type']) + .header('expires', res.headers.expires) + .header('date', res.headers.date) + .header('cache-control', res.headers['cache-control']); + } + } + }, { method: 'GET', path: '/static/{path*}', @@ -31,12 +113,21 @@ exports.register = async server => { } } }, + { + method: '*', + path: '/~server-error', + handler: { + view: { + name: 'server-error' + } + } + }, { method: '*', path: '/{path*}', - config: { - handler: (request, h) => { - return h.response(indexFile).type('text/html'); + handler: { + view: { + name: 'app' } } } diff --git a/packages/images/package.json b/packages/images/package.json index 9b19492a..114b56da 100644 --- a/packages/images/package.json +++ b/packages/images/package.json @@ -8,23 +8,28 @@ "scripts": { "dev": "REACT_APP_GQL_PORT=4000 PORT=3070 REACT_APP_GQL_PROTOCOL=http joyent-react-scripts start", "start": "PORT=3069 joyent-react-scripts start", - "build": "NODE_ENV=production joyent-react-scripts build", + "build:app": "NODE_ENV=production joyent-react-scripts build", + "build:lib": "NODE_ENV=production SSR=1 UMD=1 babel src --out-dir lib/app --copy-files", "lint-ci": "eslint . --ext .js --ext .md", "lint": "eslint . --fix --ext .js --ext .md", "test-ci": "NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'", "test": "DEFAULT_TIMEOUT_INTERVAL=100000 NODE_ENV=test joyent-react-scripts test --env=jsdom", - "prepublish": "echo 0" + "postinstall": "npm run build:app", + "prepublish": "npm run build:lib" }, "dependencies": { "@manaflair/redux-batch": "^0.1.0", "apollo": "^0.2.2", - "apollo-cache-inmemory": "^1.1.7", - "apollo-client": "^2.2.3", - "apollo-link-http": "^1.3.3", + "apollo-cache-inmemory": "^1.1.9", + "apollo-client": "^2.2.5", + "apollo-link-http": "^1.5.1", "apr-intercept": "^3.0.3", + "babel-preset-joyent-portal": "^7.0.1", "date-fns": "^1.29.0", "declarative-redux-form": "^2.0.8", "force-array": "^3.1.0", + "hapi-render-react": "^2.1.0", + "hapi-render-react-joyent-document": "^4.2.3", "joyent-logo-assets": "^1.0.0", "joyent-react-styled-flexboxgrid": "^2.2.3", "joyent-ui-toolkit": "^5.0.0", @@ -41,7 +46,7 @@ "react": "^16.2.0", "react-apollo": "^2.0.4", "react-dom": "^16.2.0", - "react-redux": "^5.0.6", + "react-redux": "^5.0.7", "react-redux-values": "^1.1.2", "react-router": "^4.2.0", "react-router-dom": "^4.2.2", @@ -51,17 +56,17 @@ "scroll-to-element": "^2.0.0", "styled-components": "^3.1.6", "styled-components-spacing": "^2.1.3", - "styled-flex-component": "^2.2.0", + "styled-flex-component": "^2.2.1", "styled-is": "^1.1.2", "title-case": "^2.1.1" }, "devDependencies": { - "babel-preset-joyent-portal": "^6.0.3", - "eslint": "^4.16.0", + "babel-cli": "^6.26.0", + "eslint": "^4.18.1", "eslint-config-joyent-portal": "^3.3.1", "jest-image-snapshot": "^2.3.0", "jest-styled-components": "^4.11.0-0", - "joyent-react-scripts": "^7.2.2", + "joyent-react-scripts": "^7.3.0", "react-screenshot-renderer": "^1.1.2", "react-test-renderer": "^16.2.0" } diff --git a/packages/images/public/index.html b/packages/images/public/index.html index f8792189..5ab0d4d6 100644 --- a/packages/images/public/index.html +++ b/packages/images/public/index.html @@ -6,16 +6,11 @@ - - + + My Joyent Images β - -
- diff --git a/packages/images/src/_aliases.js b/packages/images/src/_aliases.js new file mode 100644 index 00000000..56053b86 --- /dev/null +++ b/packages/images/src/_aliases.js @@ -0,0 +1,12 @@ +const path = require('path'); + +const { SSR } = process.env; + +const aliases = {}; + +if (SSR) { + aliases['scroll-to-element'] = './src/mocks/scroll-to-element'; + aliases['^joyent-ui-toolkit/dist/es/editor$'] = './src/mocks/editor'; +} + +module.exports = aliases; diff --git a/packages/images/src/_document.js b/packages/images/src/_document.js new file mode 100644 index 00000000..52d02767 --- /dev/null +++ b/packages/images/src/_document.js @@ -0,0 +1,34 @@ +const get = require('lodash.get'); +const Document = require('hapi-render-react-joyent-document'); +const path = require('path'); +const url = require('url'); + +const { default: theme } = require('./state/theme'); +const { default: createClient } = require('./state/apollo-client'); +const { default: createStore } = require('./state/redux-store'); + +const indexFile = path.join(__dirname, '../../build/index.html'); + +const getState = request => { + const { req, res } = request.raw; + + const _font = get(theme, 'font.href', () => ''); + const _mono = get(theme, 'monoFont.href', () => ''); + const _addr = url.parse(`http://${req.headers.host}`); + const _theme = Object.assign({}, theme, { + font: Object.assign({}, theme.font, { + href: () => _font(_addr) + }), + monoFont: Object.assign({}, theme.monoFont, { + href: () => _mono(_addr) + }) + }); + + return { + theme: _theme, + createClient, + createStore + }; +}; + +module.exports = Document({ indexFile, getState }); diff --git a/packages/images/src/app.js b/packages/images/src/app.js index 27e896a5..cf61df7e 100644 --- a/packages/images/src/app.js +++ b/packages/images/src/app.js @@ -1,21 +1,10 @@ import React from 'react'; -import { ThemeProvider } from 'styled-components'; -import { Provider as ReduxProvider } from 'react-redux'; -import { ApolloProvider } from 'react-apollo'; -import { theme, RootContainer } from 'joyent-ui-toolkit'; - -import { client, store } from '@state/store'; -import Router from '@root/router'; +import { RootContainer } from 'joyent-ui-toolkit'; +import Routes from '@root/routes'; export default () => ( - - - - - - - - - + + + ); diff --git a/packages/images/src/components/image.js b/packages/images/src/components/image.js index 3fd8d8bd..732ae624 100644 --- a/packages/images/src/components/image.js +++ b/packages/images/src/components/image.js @@ -95,7 +95,7 @@ export const Image = ({ height: '24' })} - + {name} diff --git a/packages/images/src/containers/create-image/details.js b/packages/images/src/containers/create-image/details.js index 25c8fa06..6faffcca 100644 --- a/packages/images/src/containers/create-image/details.js +++ b/packages/images/src/containers/create-image/details.js @@ -17,7 +17,7 @@ import Animated from '@containers/create-image/animated'; import Details from '@components/create-image/details'; import Description from '@components/description'; import GetRandomName from '@graphql/get-random-name.gql'; -import { client } from '@state/store'; +import createStore from '@state/apollo-client'; import { Forms } from '@root/constants'; const NameContainer = ({ @@ -111,7 +111,10 @@ const NameContainer = ({ export default compose( Animated, graphql(GetRandomName, { - fetchPolicy: 'network-only', + options: () => ({ + fetchPolicy: 'network-only', + ssr: false + }), props: ({ data }) => ({ placeholderName: data.rndName || '' }) @@ -168,7 +171,7 @@ export default compose( dispatch(set({ name: 'create-image-name-randomizing', value: true })); const [err, res] = await intercept( - client.query({ + createStore().query({ fetchPolicy: 'network-only', query: GetRandomName }) diff --git a/packages/images/src/containers/create.js b/packages/images/src/containers/create.js index 46f8ab02..f3e0c24b 100644 --- a/packages/images/src/containers/create.js +++ b/packages/images/src/containers/create.js @@ -95,6 +95,7 @@ export default compose( graphql(CreateImage, { name: 'createImage' }), graphql(GetInstance, { options: ({ match }) => ({ + ssr: false, variables: { name: get(match, 'params.instance') } diff --git a/packages/images/src/containers/list.js b/packages/images/src/containers/list.js index fc7be560..74d28f70 100644 --- a/packages/images/src/containers/list.js +++ b/packages/images/src/containers/list.js @@ -99,8 +99,10 @@ export default compose( name: 'removeImage' }), graphql(ListImages, { - fetchPolicy: 'network-only', - pollInterval: 1000, + options: () => ({ + ssr: false, + pollInterval: 1000 + }), props: ({ data: { images, loading, error, refetch } }) => ({ images, loading, diff --git a/packages/images/src/containers/summary.js b/packages/images/src/containers/summary.js index d850bf44..fd645278 100644 --- a/packages/images/src/containers/summary.js +++ b/packages/images/src/containers/summary.js @@ -67,6 +67,7 @@ export default compose( graphql(RemoveImage, { name: 'removeImage' }), graphql(GetImage, { options: ({ match }) => ({ + ssr: false, variables: { name: get(match, 'params.image') } diff --git a/packages/images/src/containers/tags.js b/packages/images/src/containers/tags.js index dcf9ff11..32e9fcb3 100644 --- a/packages/images/src/containers/tags.js +++ b/packages/images/src/containers/tags.js @@ -116,6 +116,7 @@ export default compose( }), graphql(GetTags, { options: ({ match }) => ({ + ssr: false, fetchPolicy: 'network-only', pollInterval: 1000, variables: { diff --git a/packages/images/src/index.js b/packages/images/src/index.js index 9d35a45d..02edd9c0 100644 --- a/packages/images/src/index.js +++ b/packages/images/src/index.js @@ -1,8 +1,16 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { ThemeProvider, consolidateStreamedStyles } from 'styled-components'; +import { Provider as ReduxProvider } from 'react-redux'; +import { ApolloProvider } from 'react-apollo'; +import { BrowserRouter } from 'react-router-dom'; import isFunction from 'lodash.isfunction'; import isFinite from 'lodash.isfinite'; +import { theme } from 'joyent-ui-toolkit'; + +import createStore from '@state/redux-store'; +import createClient from '@state/apollo-client'; import { register } from './sw'; import App from './app'; @@ -10,6 +18,19 @@ if (!isFunction(Number.isFinite)) { Number.isFinite = isFinite; } -ReactDOM.render(, document.getElementById('root')); +consolidateStreamedStyles(); + +ReactDOM.hydrate( + + + + + + + + + , + document.getElementById('root') +); register(); diff --git a/packages/images/src/mocks/__aliases__.js b/packages/images/src/mocks/__aliases__.js index f053ebf7..8e07c7a2 100644 --- a/packages/images/src/mocks/__aliases__.js +++ b/packages/images/src/mocks/__aliases__.js @@ -1 +1,8 @@ -module.exports = {}; +module.exports = { + '^joyent-ui-toolkit/dist/es/editor$': '/src/mocks/editor', + '^redux-form$': '/src/mocks/redux-form', + '^react-responsive$': '/src/mocks/react-responsive', + '^react-router-dom$': '/src/mocks/react-router-dom', + '^declarative-redux-form$': '/src/mocks/declarative-redux-form', + '^scroll-to-element': '/src/mocks/scroll-to-element' +}; diff --git a/packages/images/src/mocks/declarative-redux-form.js b/packages/images/src/mocks/declarative-redux-form.js new file mode 100644 index 00000000..be991de1 --- /dev/null +++ b/packages/images/src/mocks/declarative-redux-form.js @@ -0,0 +1,3 @@ +import React from 'react'; + +export default ({ children, ...props }) => React.createElement(children, props); diff --git a/packages/images/src/mocks/editor.js b/packages/images/src/mocks/editor.js new file mode 100644 index 00000000..ac7b4287 --- /dev/null +++ b/packages/images/src/mocks/editor.js @@ -0,0 +1,3 @@ +import React from 'react'; + +export default () => joyent-maifest-editor; diff --git a/packages/images/src/mocks/index.js b/packages/images/src/mocks/index.js new file mode 100644 index 00000000..cf9bab85 --- /dev/null +++ b/packages/images/src/mocks/index.js @@ -0,0 +1,3 @@ +export { default as Router } from './router'; +export { default as Store } from './store'; +export { default as Theme } from './theme'; diff --git a/packages/images/src/mocks/react-responsive.js b/packages/images/src/mocks/react-responsive.js new file mode 100644 index 00000000..27948083 --- /dev/null +++ b/packages/images/src/mocks/react-responsive.js @@ -0,0 +1,7 @@ +import React from 'react'; + +export default ({ query, children }) => ( + + {children} + +); diff --git a/packages/images/src/mocks/react-router-dom.js b/packages/images/src/mocks/react-router-dom.js new file mode 100644 index 00000000..88529e49 --- /dev/null +++ b/packages/images/src/mocks/react-router-dom.js @@ -0,0 +1,4 @@ +import React from 'react'; + +export const Field = ({ children, ...rest }) => + React.createElement('a', rest, children); diff --git a/packages/images/src/mocks/redux-form.js b/packages/images/src/mocks/redux-form.js new file mode 100644 index 00000000..df7d1a7a --- /dev/null +++ b/packages/images/src/mocks/redux-form.js @@ -0,0 +1,4 @@ +import React from 'react'; + +export const Field = ({ component = 'input', children, ...rest }) => + React.createElement(component, rest, children); diff --git a/packages/images/src/mocks/router.js b/packages/images/src/mocks/router.js new file mode 100644 index 00000000..47dbba36 --- /dev/null +++ b/packages/images/src/mocks/router.js @@ -0,0 +1,4 @@ +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; + +export default ({ children }) => {children}; diff --git a/packages/images/src/mocks/scroll-to-element.js b/packages/images/src/mocks/scroll-to-element.js new file mode 100644 index 00000000..461f67a0 --- /dev/null +++ b/packages/images/src/mocks/scroll-to-element.js @@ -0,0 +1 @@ +export default () => null; diff --git a/packages/images/src/mocks/store.js b/packages/images/src/mocks/store.js new file mode 100644 index 00000000..3579b3f4 --- /dev/null +++ b/packages/images/src/mocks/store.js @@ -0,0 +1,8 @@ +import React from 'react'; +import { ApolloProvider } from 'react-apollo'; + +import createClient from '@state/apollo-client'; + +export default ({ children }) => ( + {children} +); diff --git a/packages/images/src/mocks/theme.js b/packages/images/src/mocks/theme.js new file mode 100644 index 00000000..3204f078 --- /dev/null +++ b/packages/images/src/mocks/theme.js @@ -0,0 +1,22 @@ +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { + theme, + RootContainer, + PageContainer, + ViewContainer +} from 'joyent-ui-toolkit'; + +export default ({ children, ss }) => ( + + {ss ? ( + + + {children} + + + ) : ( + children + )} + +); diff --git a/packages/images/src/router.js b/packages/images/src/router.js deleted file mode 100644 index dc5e7a1e..00000000 --- a/packages/images/src/router.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'; -import get from 'lodash.get'; - -import { PageContainer } from 'joyent-ui-toolkit'; -import Breadcrumb from '@containers/breadcrumb'; -import Menu from '@containers/menu'; -import List from '@containers/list'; -import Summary from '@containers/summary'; -import Create from '@containers/create'; -import Tags from '@containers/tags'; -import Footer from '@components/footer'; - -export default () => ( - - - {/* Breadcrumb */} - - - - - {/* Menu */} - - - {}} /> - - {/* Images */} - - - - - ( - - )} - /> - - {/* Create Image */} - - ( - - )} - /> - - -