├── .dockerignore ├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MAINTAINING.md ├── README.md ├── babel.config.js ├── docs ├── README.md ├── _contents.json ├── guides │ ├── client-transitions.md │ ├── logging.md │ ├── production.md │ ├── react-server-cli.md │ ├── understanding-rendering.md │ ├── using-react-server-agent.md │ ├── writing-middleware.md │ └── writing-pages.md ├── intro │ ├── design.md │ └── why-react-server.md ├── page-api.md ├── testing-ports.md └── using-react-server-agent.md ├── images ├── reactserver_logo.png ├── reactserver_logo@2x.png ├── reactserver_logomark.png ├── reactserver_logomark@2x.png └── svg │ ├── reactserver_logo.svg │ └── reactserver_logomark.svg ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── babel-plugin-react-server ├── .gitignore ├── .npmignore ├── README.md ├── babel.config.js ├── package.json ├── src │ └── index.js └── test │ ├── fixtures │ ├── configurable-token │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ ├── example │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ ├── label-as-second-key │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ ├── label │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ ├── prefix │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ ├── reserved-future-tokens │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ └── trim │ │ ├── .babelrc │ │ ├── actual.js │ │ └── expected.js │ └── index.js ├── babel-preset-react-server ├── .eslintignore ├── .gitignore ├── .npmignore ├── README.md ├── index.js ├── package.json ├── test.js └── tst │ ├── expected.js │ └── source.js ├── flab ├── .eslintignore ├── .gitignore ├── LAB.src.js ├── README.md ├── index.js ├── minify.js ├── package.json └── stringify.js ├── generator-react-server ├── .gitignore ├── .npmignore ├── README.md ├── generators │ └── app │ │ ├── index.js │ │ └── templates │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── _gitignore │ │ ├── _reactserverrc │ │ ├── babel.config.js │ │ ├── components │ │ └── hello-world.js │ │ ├── docker-compose.yml │ │ ├── package.json │ │ ├── pages │ │ └── hello-world.js │ │ ├── routes.json │ │ └── test.js ├── package.json └── test │ └── app.js ├── react-server-cli ├── .babelrc ├── .gitignore ├── .npmignore ├── README.md ├── bin │ └── react-server-cli ├── gulpfile.babel.js ├── package.json ├── src │ ├── ConfigurationError.js │ ├── NonCachingExtractTextLoader.js │ ├── __tests__ │ │ └── compileClient │ │ │ └── compileClientSpec.js │ ├── callerDependency-Mock.js │ ├── callerDependency.js │ ├── cli.js │ ├── commands │ │ ├── add-page.js │ │ ├── compile.js │ │ ├── init.js │ │ └── start.js │ ├── compileClient.js │ ├── defaultOptions.js │ ├── fileExists.js │ ├── findOptionsInFiles.js │ ├── handleCompilationErrors.js │ ├── index.js │ ├── logProductionWarnings.js │ ├── mergeOptions.js │ ├── parseCliArgs.js │ ├── react-server.js │ ├── run.js │ ├── serverSideHotModuleReload.js │ ├── setupLogging.js │ └── webpack │ │ ├── webpack.config.fn.js │ │ └── webpack4.config.fn.js └── test │ ├── commands.js │ ├── fixtures │ ├── BasicPage.js │ └── commands │ │ ├── start-basic │ │ ├── in-files │ │ │ └── routes.json │ │ └── options.json │ │ ├── start-missing-routes │ │ └── options.json │ │ ├── start-reactserverrc │ │ ├── in-files │ │ │ ├── .reactserverrc │ │ │ └── customRoutes.js │ │ └── options.json │ │ └── start-routes-with-error │ │ ├── in-files │ │ └── routes.js │ │ └── options.json │ └── parseCliArgs.js ├── react-server-core-middleware ├── .gitignore ├── .npmignore ├── README.md ├── index.js ├── package.json └── src │ ├── coreCssMiddleware.js │ └── coreJsMiddleware.js ├── react-server-data-bundle-cache ├── .gitignore ├── .npmignore ├── README.md ├── gulpfile.js ├── main.js ├── package.json └── src │ ├── before.js │ ├── constants.js │ ├── fetch.js │ ├── index.js │ ├── install.js │ ├── last-url.js │ ├── listen.js │ ├── log-events.js │ ├── logging.js │ ├── opt-in.js │ ├── preload.js │ └── wrap.js ├── react-server-examples ├── .babelrc ├── .eslintignore ├── README.md ├── bike-share │ ├── .babelrc │ ├── .gitignore │ ├── .reactserverrc │ ├── Dockerfile │ ├── README.md │ ├── api │ │ ├── index.js │ │ └── networks.js │ ├── components │ │ ├── error.js │ │ ├── footer.js │ │ ├── header.js │ │ ├── network-card.js │ │ ├── network-list.js │ │ ├── station-card.js │ │ └── station-list.js │ ├── docker-compose.yml │ ├── middleware │ │ └── request-to-port.js │ ├── package.json │ ├── pages │ │ ├── index.js │ │ ├── network.js │ │ └── not-found.js │ ├── routes.js │ └── styles │ │ ├── base.scss │ │ ├── index.scss │ │ ├── network.scss │ │ └── reset.scss ├── code-splitting │ ├── .babelrc │ ├── .gitignore │ ├── .reactserverrc │ ├── README.md │ ├── componentLoaders │ │ ├── FooterLoader.js │ │ └── HeaderLoader.js │ ├── components │ │ ├── Body.js │ │ ├── Footer.js │ │ └── Header.js │ ├── package.json │ ├── pages │ │ └── index.js │ ├── routes.js │ ├── webpackConfig.js │ └── yarn.lock ├── hello-world │ ├── .babelrc │ ├── .gitignore │ ├── .reactserverrc │ ├── HelloWorld.js │ ├── HelloWorldPage.js │ ├── README.md │ ├── package.json │ ├── routes.js │ └── yarn.lock ├── meteor-site │ ├── .babelrc │ ├── .gitignore │ ├── .reactserverrc │ ├── README.md │ ├── components │ │ ├── Footer.js │ │ ├── Header.js │ │ ├── Map.js │ │ ├── MeteorMap.js │ │ ├── MeteorTable.js │ │ ├── Table.js │ │ └── index.js │ ├── middleware │ │ └── Bootstrap.js │ ├── package.json │ ├── pages │ │ └── Homepage.js │ ├── routes.json │ ├── stores │ │ ├── actions.js │ │ ├── index.js │ │ └── reducers.js │ └── styles │ │ ├── general.css │ │ └── meteor.svg ├── package.json ├── redux-basic │ ├── .babelrc │ ├── .gitignore │ ├── .reactserverrc │ ├── README.md │ ├── components │ │ └── Counter.js │ ├── package.json │ ├── pages │ │ ├── counter-app │ │ │ ├── index.js │ │ │ └── reducer.js │ │ └── store.js │ └── routes.js └── styled-components │ ├── .babelrc │ ├── .eslintrc │ ├── .gitignore │ ├── .reactserverrc │ ├── README.md │ ├── components │ ├── banner.js │ ├── centered-div.js │ ├── hello-world.js │ ├── primary-button.js │ ├── secondary-button.js │ └── themes.js │ ├── package.json │ ├── pages │ └── hello-world.js │ ├── routes.json │ ├── styles │ ├── button.js │ └── global.js │ ├── test.js │ └── yarn.lock ├── react-server-gulp-module-tagger ├── .npmignore ├── README.md ├── index.js ├── package.json └── test │ ├── fixtures │ ├── custom-token │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ ├── example │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ ├── prefix │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ ├── reserved-future-tokens │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ ├── single-quote-custom-token │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ └── trim │ │ ├── actual.js │ │ ├── expected.js │ │ └── gulpfile.js │ └── test.js ├── react-server-integration-tests ├── .babelrc ├── .gitignore ├── README.md ├── config.json ├── gulpfile.babel.js ├── package.json └── src │ ├── __tests__ │ ├── asyncRender │ │ ├── AsyncElementPage.js │ │ ├── AsyncRenderSpec.js │ │ └── ServerTimeoutElementPage.js │ ├── containers │ │ ├── ContainersSpec.js │ │ ├── components │ │ │ └── ValEcho.js │ │ ├── data │ │ │ └── FooEmitter.js │ │ └── pages │ │ │ ├── AttributesOnRootContainer.js │ │ │ ├── AttributesOnRootElement.js │ │ │ ├── PropsFromEmitterRootContainer.js │ │ │ ├── PropsFromEmitterRootElement.js │ │ │ ├── SingleDivInArray.js │ │ │ ├── SingleDivNoArray.js │ │ │ ├── SinglePromiseInArray.js │ │ │ ├── SinglePromiseNoArray.js │ │ │ ├── SingleRootContainerInArray.js │ │ │ ├── SingleRootContainerNoArray.js │ │ │ ├── SingleRootElementInArray.js │ │ │ ├── SingleRootElementInRootContainer.js │ │ │ └── SingleRootElementNoArray.js │ ├── css │ │ ├── BasicCss.css │ │ ├── CssPage.js │ │ ├── CssSpec.js │ │ ├── CssWithAssets.css │ │ ├── CssWithAssetsPage.js │ │ ├── fonts │ │ │ ├── Open Font License.markdown │ │ │ ├── Prociono-Regular-webfont.eot │ │ │ ├── Prociono-Regular-webfont.svg │ │ │ ├── Prociono-Regular-webfont.ttf │ │ │ └── Prociono-Regular-webfont.woff │ │ └── images │ │ │ └── cat.png │ ├── httpAndHttpsServers │ │ ├── HelloWorldPage.js │ │ ├── HttpServerSpec.js │ │ ├── cert.pem │ │ └── key.pem │ ├── httpHeaders │ │ ├── AsyncHttpHeadersPage.js │ │ ├── HttpHeadersPage.js │ │ └── HttpHeadersSpec.js │ ├── internalServerError │ │ ├── InternalServerErrorSpec.js │ │ └── pages │ │ │ ├── InternalServerErrorException.js │ │ │ ├── InternalServerErrorNoDocument.js │ │ │ ├── InternalServerErrorRejection.js │ │ │ └── InternalServerErrorWithDocument.js │ ├── notFound │ │ ├── NotFoundSpec.js │ │ └── pages │ │ │ ├── NotFoundNoDocument.js │ │ │ └── NotFoundWithDocument.js │ ├── rawResponse │ │ ├── RawResponsePage.js │ │ └── RawResponseSpec.js │ ├── redirectForward │ │ ├── FinalPage.js │ │ ├── ForwardPage.js │ │ ├── PermanentRedirectPage.js │ │ ├── PermanentRedirectWithDocumentPage.js │ │ ├── RedirectForwardSpec.js │ │ ├── TemporaryRedirectPage.js │ │ └── TemporaryRedirectWithDocumentPage.js │ ├── simpleRender │ │ ├── GoodbyeWorldPage.js │ │ ├── HelloWorldPage.js │ │ ├── HelloWorldSpec.js │ │ └── MultiElementPage.js │ └── title │ │ ├── AsyncServerTimeoutTitlePage.js │ │ ├── AsyncTitlePage.js │ │ ├── NullTitlePage.js │ │ ├── SimpleTitlePage.js │ │ ├── TitleSpec.js │ │ └── UnicodeTitlePage.js │ └── specRuntime │ ├── TransitionPage.js │ └── testHelper.js ├── react-server-middleware-json-response ├── .gitignore ├── README.md ├── index.src.js └── package.json ├── react-server-module-tagger ├── README.md ├── index.js ├── package.json └── test.js ├── react-server-redux ├── .gitignore ├── .npmignore ├── gulpfile.babel.js ├── package.json ├── src │ ├── ReduxAdapter.js │ ├── RootProvider.js │ └── index.js └── test │ └── ReduxAdapter.spec.js ├── react-server-test-pages ├── .eslintignore ├── .gitignore ├── .npmignore ├── .reactserverrc ├── README.md ├── babel.config.js ├── entrypoints.js ├── middleware │ └── RequestToPort.js ├── package.json ├── pages │ ├── bottleneck │ │ ├── colors │ │ │ ├── blue.scss │ │ │ ├── green.scss │ │ │ ├── indigo.scss │ │ │ ├── orange.scss │ │ │ ├── purple.scss │ │ │ ├── red.scss │ │ │ └── yellow.scss │ │ ├── dataRequests.js │ │ ├── elements.js │ │ ├── largeDataRequests.js │ │ ├── middleware.js │ │ └── test-middleware │ │ │ └── TestMiddleware.js │ ├── data │ │ ├── EchoCssPage.js │ │ ├── delay.js │ │ └── error.js │ ├── error │ │ └── logs.js │ ├── index.js │ ├── navigation │ │ ├── common.css │ │ ├── common.js │ │ ├── data-bundle-cache.css │ │ ├── data-bundle-cache.js │ │ ├── forward.js │ │ ├── forwardEven.js │ │ ├── forwardOdd.js │ │ ├── navigateTo.js │ │ ├── playground.css │ │ └── playground.js │ ├── root │ │ ├── aboveTheFold.js │ │ ├── attributes.js │ │ ├── error.js │ │ ├── order.js │ │ ├── reduxAdapter.js │ │ ├── reuse-trim.js │ │ ├── rootProvider.js │ │ └── when.js │ └── styles │ │ ├── promises.js │ │ ├── sass.js │ │ ├── sass.sass │ │ └── sass.scss └── routes.js ├── react-server-website ├── .babelrc ├── .dockerignore ├── .eslintignore ├── .gitignore ├── .reactserverrc ├── Dockerfile ├── README.md ├── build-dir-contents.js ├── components │ ├── Footer.js │ ├── Footer.less │ ├── Header.js │ ├── Header.less │ ├── Markdown.js │ ├── Markdown.less │ ├── assets │ │ ├── SvgClose.js │ │ ├── SvgDropdown.js │ │ ├── SvgHambut.js │ │ └── SvgLogo.js │ ├── content │ │ ├── HomeContributingSection.md │ │ ├── HomeGetStartedSection.md │ │ └── HomeWhySection.md │ ├── doc-body.js │ ├── doc-contents.js │ ├── doc-contents.less │ ├── homepage-body.js │ ├── homepage-body.less │ ├── page-title.js │ ├── page-title.less │ ├── source-body.js │ ├── source-body.less │ ├── source-contents.js │ └── source-intro.md ├── deployment │ ├── decrypt_credentials.py │ ├── deploy.yml │ ├── docker-compose.yml │ ├── ec2.ini │ ├── ec2.py │ ├── encrypt_credentials.py │ ├── instance-setup.sh │ ├── requirements.txt │ └── setup.yml ├── docker-compose.yml ├── favicon.ico ├── lib │ ├── github-button.js │ ├── highlight.js │ ├── page-name-mixin.js │ ├── repo.js │ └── slackin-button.js ├── middleware │ ├── Analytics.js │ ├── CacheControl.js │ ├── DataBundleCache.js │ ├── Favicon.js │ ├── PageFooter.js │ ├── PageHeader.js │ ├── RequestToPort.js │ ├── Theme.js │ └── Theme │ │ ├── base.less │ │ ├── button.less │ │ ├── constants.less │ │ └── layout.less ├── nginx.conf ├── package.json ├── pages │ ├── contents-api.js │ ├── docs-api.js │ ├── docs.js │ ├── docs.less │ ├── homepage.js │ ├── homepage.less │ ├── source-api.js │ ├── source-contents-api.js │ ├── source.js │ └── source.less ├── routes.js ├── running-with-docker-compose.md └── test.js └── react-server ├── .gitignore ├── .npmignore ├── README.md ├── core ├── ClientController.js ├── ClientRequest.js ├── ExpressServerRequest.js ├── ReactServerAgent.js ├── ReactServerAgent │ ├── Cache.js │ ├── Plugins.js │ ├── Request.js │ └── __tests__ │ │ └── ReactServerAgentSpec.js ├── __bench__ │ ├── handlePage.js │ └── loggingClient.js ├── __tests__ │ ├── ClientRequestSpec.js │ ├── NormalValuesPage.js │ ├── NullValuePromisesPage.js │ ├── NullValuesPage.js │ ├── context │ │ └── navigator │ │ │ ├── DumbPage.js │ │ │ ├── NavigatorRoutes.js │ │ │ └── NavigatorSpec.js │ └── reactMiddlewareSpec.js ├── client.js ├── common.js ├── components │ ├── FragmentDataCache.jsx │ ├── History.js │ ├── Link.jsx │ ├── RootContainer.js │ ├── RootElement.js │ ├── TheFold.js │ └── __tests__ │ │ └── FragmentDataCacheSpec.js ├── config.js ├── constants.js ├── context │ ├── Navigator.js │ └── RequestContext.js ├── logging.js ├── logging │ ├── client.js │ ├── common.js │ ├── response.js │ ├── server.js │ └── stats.js ├── renderMiddleware.js ├── server.js ├── test │ └── util │ │ └── reactServerAgentSupport.js └── util │ ├── ClientCssHelper.js │ ├── DebugUtil.js │ ├── PageUtil.js │ ├── RequestLocalStorage.js │ ├── StringEscapeUtil.js │ ├── bundleNameUtil.js │ └── navigateTo.js ├── gulpfile.js └── package.json /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/Dockerfile* 3 | **/.dockerignore 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.py linguist-vendored 3 | *.md linguist-documentation 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules 3 | .changelog 4 | *.swp 5 | *.orig 6 | asini-debug.log 7 | *npm-debug.log* 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6.3.0 2 | 3 | WORKDIR /usr/src/react-server/ 4 | 5 | ENV NODE_ENV=development 6 | 7 | # NPM install package.json file 8 | COPY package.json package.json 9 | RUN npm install 10 | 11 | # Bootstrap everything else 12 | COPY . . 13 | RUN npm run bootstrap 14 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | 4 | const presets = [ 5 | 'react-server' 6 | ]; 7 | 8 | return { 9 | presets, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /docs/_contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "contents": [ 3 | { 4 | "name": "Introduction", 5 | "pages": [ 6 | { "name": "Why React Server", "path": "intro/why-react-server" }, 7 | { "name": "Design Principles", "path": "intro/design" } 8 | ] 9 | }, 10 | { 11 | "name": "Guides", 12 | "pages": [ 13 | { "name": "Writing Pages", "path": "guides/writing-pages" }, 14 | { "name": "How Logging Works", "path": "guides/logging" }, 15 | { "name": "Writing Middleware", "path": "guides/writing-middleware" }, 16 | { "name": "Understanding Rendering", "path": "guides/understanding-rendering" }, 17 | { "name": "Using ReactServerAgent", "path": "guides/using-react-server-agent"}, 18 | { "name": "Using the React Server CLI", "path": "guides/react-server-cli"}, 19 | { "name": "Performing Client Transitions", "path": "guides/client-transitions"}, 20 | { "name": "Running a Production Server", "path": "guides/production"} 21 | ] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /docs/testing-ports.md: -------------------------------------------------------------------------------- 1 | We need to be certain there aren't any packages that run tests on 2 | the same port, as our CI test target runs many packages' tests 3 | simultaneously, so port conflicts will fail the tests. Make sure 4 | to update this manifest when adding a test that starts a server. 5 | 6 | ## generator-react-server 7 | 3000, 3001 8 | 9 | ## react-server-integration-test 10 | 8771, 8772 11 | 12 | ## react-server-website 13 | 3010, 3011 14 | -------------------------------------------------------------------------------- /images/reactserver_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redfin/react-server/572ff229d51d25515ab4b8f13cd15a87cd787f61/images/reactserver_logo.png -------------------------------------------------------------------------------- /images/reactserver_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redfin/react-server/572ff229d51d25515ab4b8f13cd15a87cd787f61/images/reactserver_logo@2x.png -------------------------------------------------------------------------------- /images/reactserver_logomark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redfin/react-server/572ff229d51d25515ab4b8f13cd15a87cd787f61/images/reactserver_logomark.png -------------------------------------------------------------------------------- /images/reactserver_logomark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redfin/react-server/572ff229d51d25515ab4b8f13cd15a87cd787f61/images/reactserver_logomark@2x.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.13.2", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "1.0.0-alpha.2", 7 | "command": { 8 | "bootstrap": { 9 | "hoist": true 10 | } 11 | }, 12 | "changelog": { 13 | "repo": "redfin/react-server", 14 | "labels": { 15 | "breaking change": "Breaking change", 16 | "enhancement": "Enhancement", 17 | "performance": "Performance", 18 | "bug": "Bug fix", 19 | "documentation": "Documentation", 20 | "security": "Security" 21 | }, 22 | "cacheDir": ".changelog" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | lib 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | src 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | 4 | const presets = [ 5 | "@babel/preset-env", 6 | "@babel/preset-react", 7 | ]; 8 | const plugins = [ 9 | "@babel/plugin-transform-runtime" 10 | ]; 11 | 12 | return { 13 | presets, 14 | plugins, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-react-server", 3 | "version": "1.0.0-alpha.2", 4 | "description": "Babel plugin for React Server transpilation", 5 | "repository": "redfin/babel-plugin-react-server", 6 | "author": "Doug Wade ", 7 | "main": "lib/index.js", 8 | "dependencies": { 9 | "@babel/types": "^7.0.0", 10 | "react-server-module-tagger": "^1.0.0-alpha.2" 11 | }, 12 | "scripts": { 13 | "clean": "rm -rf lib npm-debug.log*", 14 | "lint": "eslint src test/index.js", 15 | "build": "babel --root-mode upward src -d lib", 16 | "test": "mocha --compilers js:@babel/register", 17 | "test:watch": "npm run test -- --watch", 18 | "prepublish": "npm run clean && npm run build" 19 | }, 20 | "keywords": [ 21 | "react", 22 | "server", 23 | "babel", 24 | "plugin", 25 | "babel-plugin" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/configurable-token/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src", { "tokens": ["BAR"] }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/configurable-token/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server').logging.getLogger(BAR); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/configurable-token/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | name: "babel-plugin-react-server.test.fixtures.configurable-token.actual", 5 | color: { 6 | server: 133, 7 | client: "rgb(127,42,127)" 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/example/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server').logging.getLogger(__LOGGER__); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/example/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | name: "babel-plugin-react-server.test.fixtures.example.actual", 5 | color: { 6 | server: 131, 7 | client: "rgb(127,42,42)" 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label-as-second-key/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label-as-second-key/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server') 2 | .logging 3 | .getLogger(__LOGGER__({ 4 | custom: 'custom', 5 | label: 'foo' 6 | })); 7 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label-as-second-key/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | custom: "custom", 5 | label: "foo", 6 | name: "babel-plugin-react-server.test.fixtures.label-as-second-key.actual.foo", 7 | color: { 8 | server: 207, 9 | client: "rgb(212,42,212)" 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server').logging.getLogger(__LOGGER__({ label: "foo" })); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/label/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | label: "foo", 5 | name: "babel-plugin-react-server.test.fixtures.label.actual.foo", 6 | color: { 7 | server: 85, 8 | client: "rgb(42,212,127)" 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/prefix/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src", { "prefix": "nicolas-lodeiro." }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/prefix/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server').logging.getLogger(__LOGGER__); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/prefix/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | name: "nicolas-lodeiro.babel-plugin-react-server.test.fixtures.prefix.actual", 5 | color: { 6 | server: 207, 7 | client: "rgb(212,42,212)" 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/reserved-future-tokens/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src"] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/reserved-future-tokens/actual.js: -------------------------------------------------------------------------------- 1 | __CHANNEL__ 2 | __CACHE__ 3 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/reserved-future-tokens/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | ({ 4 | name: "babel-plugin-react-server.test.fixtures.reserved-future-tokens.actual", 5 | color: { 6 | server: 159, 7 | client: "rgb(127,212,212)" 8 | } 9 | }); 10 | ({ 11 | name: "babel-plugin-react-server.test.fixtures.reserved-future-tokens.actual", 12 | color: { 13 | server: 159, 14 | client: "rgb(127,212,212)" 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/trim/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["../../../src", { "trim": "babel-plugin-react-server.test.fixtures." }] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/trim/actual.js: -------------------------------------------------------------------------------- 1 | var logger = require('react-server').logging.getLogger(__LOGGER__); 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/fixtures/trim/expected.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var logger = require('react-server').logging.getLogger({ 4 | name: "trim.actual", 5 | color: { 6 | server: 207, 7 | client: "rgb(212,42,212)" 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-react-server/test/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | import assert from 'assert'; 4 | import { transformFileSync } from '@babel/core'; 5 | 6 | function trim(str) { 7 | return str.replace(/^\s+|\s+$/, ''); 8 | } 9 | 10 | describe('React Server transpilation', () => { 11 | const fixturesDir = path.join(__dirname, 'fixtures'); 12 | fs.readdirSync(fixturesDir).map((caseName) => { 13 | it(`should ${caseName.split('-').join(' ')}`, () => { 14 | const fixtureDir = path.join(fixturesDir, caseName); 15 | const actualPath = path.join(fixtureDir, 'actual.js'); 16 | const actual = transformFileSync(actualPath).code; 17 | 18 | const expected = fs.readFileSync( 19 | path.join(fixtureDir, 'expected.js') 20 | ).toString(); 21 | 22 | assert.equal(trim(actual), trim(expected)); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/.eslintignore: -------------------------------------------------------------------------------- 1 | tst 2 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | npm-debug.log* 4 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/README.md: -------------------------------------------------------------------------------- 1 | # babel-preset-react-server 2 | 3 | A babel preset for working with react-server 4 | 5 | # Usage 6 | 7 | npm install --save-dev babel-preset-react-server 8 | 9 | Then, in your .babelrc 10 | 11 | { 12 | "presets": ["babel-preset-react-server"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | return { 3 | presets: [ 4 | require('@babel/preset-env'), 5 | require('@babel/preset-react'), 6 | ], 7 | plugins: [ 8 | require('babel-plugin-react-server'), 9 | require('@babel/plugin-proposal-function-bind'), 10 | require('@babel/plugin-transform-runtime'), 11 | ], 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-react-server", 3 | "version": "1.0.0-alpha.2", 4 | "description": "A recommended Babel config for working with react-server", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rimraf npm-debug.log*", 8 | "lint": "eslint .", 9 | "prepublish": "npm run clean" 10 | }, 11 | "repository": "redfin/react-server", 12 | "keywords": [ 13 | "babel", 14 | "react-server", 15 | "react", 16 | "preset" 17 | ], 18 | "author": "Doug Wade ", 19 | "license": "Apache License 2.0", 20 | "dependencies": { 21 | "@babel/plugin-proposal-function-bind": "^7.2.0", 22 | "@babel/plugin-transform-runtime": "^7.4.3", 23 | "@babel/preset-env": "^7.0.0", 24 | "@babel/preset-react": "^7.0.0", 25 | "@babel/runtime": "^7.0.0", 26 | "babel-plugin-react-server": "^1.0.0-alpha.2" 27 | }, 28 | "devDependencies": { 29 | "rimraf": "^2.5.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import fs from 'fs'; 3 | import { transform } from '@babel/core'; 4 | 5 | test('module can be required', t => { 6 | try { 7 | require('.'); 8 | t.pass(); 9 | } catch (e) { 10 | console.error(e); //eslint-disable-line no-console 11 | t.fail(); 12 | } 13 | }); 14 | 15 | test('transpiles as expected', async t => { 16 | const source = await readFile('tst/source.js'); 17 | const actual = transform(source, { presets: [require('.')] }).code; 18 | const expected = await readFile('tst/expected.js'); 19 | t.is(trim(actual), trim(expected)); 20 | }); 21 | 22 | function readFile(filename) { 23 | return new Promise((resolve, reject) => { 24 | fs.readFile(filename, (err, data) => { 25 | if (err) { 26 | console.error(err); //eslint-disable-line no-console 27 | reject(err); 28 | } else { 29 | resolve(data.toString()); 30 | } 31 | }); 32 | }); 33 | } 34 | 35 | function trim(str) { 36 | return str.replace(/^\s+|\s+$/, ''); 37 | } 38 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/tst/expected.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _reactServer = require('react-server'); 4 | 5 | var label = 'foo'; 6 | var logger = _reactServer.logging.getLogger({"name":"unknown","color":{"server":207,"client":"rgb(212,42,212)"}}); 7 | var fooLogger = _reactServer.logging.getLogger({"name":"unknown","color":{"server":207,"client":"rgb(212,42,212)"}}({ label: label })); 8 | -------------------------------------------------------------------------------- /packages/babel-preset-react-server/tst/source.js: -------------------------------------------------------------------------------- 1 | import {logging} from 'react-server'; 2 | 3 | const label = 'foo'; 4 | const logger = logging.getLogger(__LOGGER__); 5 | const fooLogger = logging.getLogger(__LOGGER__({ label })); 6 | -------------------------------------------------------------------------------- /packages/flab/.eslintignore: -------------------------------------------------------------------------------- 1 | string 2 | LAB.src.js 3 | -------------------------------------------------------------------------------- /packages/flab/.gitignore: -------------------------------------------------------------------------------- 1 | string/ 2 | -------------------------------------------------------------------------------- /packages/flab/README.md: -------------------------------------------------------------------------------- 1 | Future Loading and Blocking JS 2 | 3 | This is based on the awesome [LABjs](https://github.com/getify/LABjs) package. 4 | 5 | TODO: Flesh out this README. :grin: 6 | -------------------------------------------------------------------------------- /packages/flab/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | src: require("./string/src"), 3 | min: require("./string/min"), 4 | } 5 | -------------------------------------------------------------------------------- /packages/flab/minify.js: -------------------------------------------------------------------------------- 1 | require("get-stdin")().then(src => console.log( // eslint-disable-line no-console 2 | "\n"+ 3 | "/*! LAB.js (LABjs :: Loading And Blocking JavaScript)\n"+ 4 | " v2.0.3 (c) Kyle Simpson\n"+ 5 | " MIT License\n"+ 6 | "*/\n"+ 7 | require("uglify-js").minify( 8 | src 9 | 10 | // Remove a few hand-annotated debug-related chunks. 11 | .replace(/\/\*!START_DEBUG(?:.|[\n\r])*?END_DEBUG\*\//g, "") 12 | 13 | 14 | // Let Uglify's dead-code elimination handle the rest. 15 | .replace(/\w+\[_Debug]/g, "false"), 16 | 17 | {fromString: true} 18 | ).code 19 | )); 20 | -------------------------------------------------------------------------------- /packages/flab/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flab", 3 | "version": "1.0.0-alpha.2", 4 | "description": "Future Loading and Blocking JS", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js", 8 | "string" 9 | ], 10 | "scripts": { 11 | "prepublish": "npm run build", 12 | "build": "mkdir -p string && npm run build-src && npm run build-min", 13 | "build-src": "node stringify.js < LAB.src.js > string/src.js", 14 | "build-min": "node minify.js < LAB.src.js | node stringify.js > string/min.js", 15 | "clean": "rimraf string", 16 | "lint": "eslint .", 17 | "test": "echo \"No tests yet...\"" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/redfin/react-server.git" 22 | }, 23 | "keywords": [ 24 | "loading", 25 | "blocking", 26 | "js", 27 | "asynchronous", 28 | "loader" 29 | ], 30 | "author": "Bo Borgerson", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/redfin/react-server/issues" 34 | }, 35 | "homepage": "https://github.com/redfin/react-server#readme" 36 | } 37 | -------------------------------------------------------------------------------- /packages/flab/stringify.js: -------------------------------------------------------------------------------- 1 | require("get-stdin")().then(src => console.log( // eslint-disable-line no-console 2 | 'module.exports = ' + src 3 | .replace(/\\/g, '\\\\') 4 | .replace(/"/g, '\\"') 5 | .replace(/^/gm, '"') 6 | .replace(/$/gm, '\\n"+')+ 7 | '"";' 8 | )); 9 | -------------------------------------------------------------------------------- /packages/generator-react-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | _eslintrc 4 | -------------------------------------------------------------------------------- /packages/generator-react-server/.npmignore: -------------------------------------------------------------------------------- 1 | coverage 2 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:slim 2 | 3 | EXPOSE 3000 4 | ENV NODE_ENV=docker-dev 5 | VOLUME /www -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/_gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | __clientTemp 3 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/_reactserverrc: -------------------------------------------------------------------------------- 1 | { 2 | "routesFile": "./routes.json", 3 | "host": "localhost", 4 | "port": 3000, 5 | "hot": true, 6 | "minify": false, 7 | "longTermCaching": false, 8 | "compileOnStartup": true, 9 | "https": false, 10 | "logLevel": "debug", 11 | "env": { 12 | "staging": { 13 | "port": "4000" 14 | }, 15 | "production": { 16 | "port": "80" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | 4 | const presets = [ 5 | "react-server", 6 | ]; 7 | const plugins = [ 8 | ]; 9 | 10 | return { 11 | presets, 12 | plugins, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/components/hello-world.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {logging} from 'react-server'; 3 | 4 | const logger = logging.getLogger(__LOGGER__); 5 | 6 | export default class HelloWorld extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {exclamationCount: 0}; 10 | this.handleClick = () => { 11 | logger.info(`Getting more excited! previously ${this.state.exclamationCount} excitements.`); 12 | this.setState({exclamationCount: this.state.exclamationCount + 1}); 13 | }; 14 | } 15 | 16 | render() { 17 | return ( 18 |
19 |

Hello, World{'!'.repeat(this.state.exclamationCount)}

20 | 21 |
22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= name %>", 3 | "version": "0.0.1", 4 | "description": "A react-server instance", 5 | "main": "HelloWorld.js", 6 | "scripts": { 7 | "start": "react-server start", 8 | "lint": "eslint test.js pages", 9 | "test": "npm run lint && ava test.js" 10 | }, 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "@babel/core": "^7.0.0", 14 | "@babel/runtime": "^7.0.0", 15 | "babel-preset-react-server": "^1.0.0-alpha.2", 16 | "react": "^15.4.1", 17 | "react-dom": "^15.4.1", 18 | "react-server": "^1.0.0-alpha.2", 19 | "react-server-cli": "^1.0.0-alpha.2", 20 | "superagent": "1.8.4" 21 | }, 22 | "devDependencies": { 23 | "ava": "^0.17.0", 24 | "babel-eslint": "^7.0.0", 25 | "eslint": "^4.0.0", 26 | "eslint-plugin-react": "^7.12.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/pages/hello-world.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import HelloWorld from '../components/hello-world'; 3 | 4 | export default class SimplePage { 5 | getElements() { 6 | return ; 7 | } 8 | 9 | getMetaTags() { 10 | return [ 11 | {charset: 'utf8'}, 12 | {'http-equiv': 'x-ua-compatible', 'content': 'ie=edge'}, 13 | {name: 'viewport', content: 'width=device-width, initial-scale=1'}, 14 | {name: 'description', content: 'hello world, powered by React Server'}, 15 | {name: 'generator', content: 'React Server'}, 16 | ]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "middleware": [], 3 | "routes": { 4 | "Homepage": { 5 | "path": "/", 6 | "page": "pages/hello-world.js" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/generator-react-server/generators/app/templates/test.js: -------------------------------------------------------------------------------- 1 | import cp from 'child_process'; 2 | import http from 'http'; 3 | import test from 'ava'; 4 | 5 | let rs; 6 | 7 | test.before('start the server', async () => { 8 | rs = cp.spawn('npm', ['start']); 9 | rs.stderr.on('data', data => console.error(data.toString())); 10 | rs.stdout.on('data', data => console.log(data.toString())); 11 | await sleep(15000); 12 | }); 13 | 14 | test('server is running', async t => { 15 | t.is(200, await getResponseCode('/')); 16 | }); 17 | 18 | test.after.always('shut down the server', async () => { 19 | rs.kill(); 20 | }); 21 | 22 | // gets the response code for an http request 23 | function getResponseCode(url) { 24 | return new Promise((resolve, reject) => { 25 | const req = http.get({ 26 | hostname: 'localhost', 27 | port: 3000, 28 | path: url, 29 | }, res => { 30 | resolve(res.statusCode); 31 | }); 32 | req.on('error', e => reject(e)); 33 | }); 34 | } 35 | 36 | function sleep(time) { 37 | return new Promise(resolve => { 38 | setTimeout(resolve, time); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /packages/react-server-cli/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "react-hot-loader/babel" 4 | ], 5 | "presets": [ 6 | "react-server" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-server-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target 3 | test/**/tmp 4 | -------------------------------------------------------------------------------- /packages/react-server-cli/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log* 3 | -------------------------------------------------------------------------------- /packages/react-server-cli/README.md: -------------------------------------------------------------------------------- 1 | # react-server-cli 2 | 3 | A simple command line app that will compile a routes file for the client and 4 | start up express. To use: 5 | 6 | 1. `npm install --save react-server-cli react-server` 7 | 2. Add `./node_modules/react-server-cli/bin/react-server-cli` as the value for 8 | `scripts.start` in package.json. 9 | 3. `npm start` from the directory that has your routes.js file. 10 | 11 | 12 | ## What It Does 13 | 14 | The CLI builds and runs a `react-server` project, using Express. It compiles 15 | JS(X) and CSS into efficiently loadable bundles with code splitting using 16 | webpack, and it supports hot reloading of React components on the client-side 17 | during development. 18 | 19 | ## Find out more 20 | 21 | See the 22 | [React Server CLI guide](http://react-server.io/docs/guides/react-server-cli) 23 | -------------------------------------------------------------------------------- /packages/react-server-cli/bin/react-server-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("../target/cli.js"); 4 | -------------------------------------------------------------------------------- /packages/react-server-cli/gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from "gulp"; 2 | import babel from "gulp-babel"; 3 | import changed from "gulp-changed"; 4 | import jasmine from "gulp-jasmine"; 5 | import logging from "react-server-gulp-module-tagger"; 6 | 7 | gulp.task("default", () => { 8 | var dest = "target"; 9 | return gulp.src("src/**/*.js") 10 | .pipe(changed(dest)) 11 | .pipe(logging()) 12 | .pipe(babel({ 13 | rootMode: "upward", 14 | })) 15 | .pipe(gulp.dest(dest)); 16 | }); 17 | 18 | gulp.task("test", ["default"], () => { 19 | return gulp.src("target/__tests__/**/*[Ss]pec.js") 20 | .pipe(jasmine({})); 21 | }); 22 | 23 | gulp.task("watch", () => { 24 | gulp.watch("src/**/*.js", ['default']); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/ConfigurationError.js: -------------------------------------------------------------------------------- 1 | // Can't use `instanceof` with babel-ified subclasses of builtins. 2 | // 3 | // https://phabricator.babeljs.io/T3083 4 | // 5 | // Gotta do this the old-fashioned way. :p 6 | // 7 | export default function ConfigurationError(message) { 8 | this.name = 'ConfigurationError'; 9 | this.message = message; 10 | this.stack = (new Error()).stack; 11 | } 12 | ConfigurationError.prototype = Object.create(Error.prototype); 13 | ConfigurationError.prototype.constructor = ConfigurationError; 14 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/NonCachingExtractTextLoader.js: -------------------------------------------------------------------------------- 1 | var ExtractTextLoader = require("extract-text-webpack-plugin/loader"); 2 | 3 | // we're going to patch the extract text loader at runtime, forcing it to stop caching 4 | // the caching causes bug #49, which leads to "contains no content" bugs. This is 5 | // risky with new version of ExtractTextPlugin, as it has to know a lot about the implementation. 6 | 7 | module.exports = function(source) { 8 | this.cacheable = false; 9 | return ExtractTextLoader.call(this, source); 10 | } 11 | 12 | module.exports.pitch = function(request) { 13 | this.cacheable = false; 14 | return ExtractTextLoader.pitch.call(this, request); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/callerDependency-Mock.js: -------------------------------------------------------------------------------- 1 | import lookup from "look-up"; 2 | import path from "path"; 3 | 4 | export default function callerDependency(dep) { 5 | // TODO: We should grab stuff based on what the routes file would get out 6 | // of `require.resolve(dep)`. Using `process.cwd()` instead for now. 7 | return lookup("packages/" + dep, {cwd: path.resolve(process.cwd() + '/..')}); 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/callerDependency.js: -------------------------------------------------------------------------------- 1 | import lookup from "look-up"; 2 | 3 | // NOTE: if this function changes, make sure it also changes in the 'callerDependency-Mock.js' file as well. 4 | export default function callerDependency(dep) { 5 | // TODO: We should grab stuff based on what the routes file would get out 6 | // of `require.resolve(dep)`. Using `process.cwd()` instead for now. 7 | return lookup("node_modules/" + dep, {cwd: process.cwd()}); 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/cli.js: -------------------------------------------------------------------------------- 1 | require("@babel/register")({ 2 | rootMode: "upward", 3 | }); 4 | 5 | const cli = require("."); 6 | cli.parseCliArgs().then((options) => { 7 | const commandResult = cli.run(options); 8 | return (options.command === "start") ? commandResult.started : commandResult; 9 | }).catch(console.error); 10 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/commands/compile.js: -------------------------------------------------------------------------------- 1 | import compileClient from "../compileClient" 2 | import handleCompilationErrors from "../handleCompilationErrors"; 3 | import setupLogging from "../setupLogging"; 4 | import logProductionWarnings from "../logProductionWarnings"; 5 | import {logging} from "../react-server"; 6 | 7 | const logger = logging.getLogger(__LOGGER__); 8 | 9 | export default function compile(options){ 10 | setupLogging(options); 11 | logProductionWarnings(options); 12 | 13 | const {compiler} = compileClient(options); 14 | 15 | logger.notice("Starting compilation of client JavaScript..."); 16 | compiler.run((err, stats) => { 17 | const error = handleCompilationErrors(err, stats); 18 | if (!error) { 19 | logger.notice("Successfully compiled client JavaScript."); 20 | } else { 21 | logger.error(error); 22 | } 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/fileExists.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | 3 | export default function fileExists(fn) { 4 | try { 5 | fs.statSync(fn); 6 | return true; 7 | } catch (e) { 8 | return false; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const run = require("./run").default; 3 | const parseCliArgs = require("./parseCliArgs").default; 4 | const defaultOptions = require("./defaultOptions").default; 5 | 6 | require.extensions['.css'] = 7 | require.extensions['.less'] = 8 | require.extensions['.scss'] = 9 | require.extensions['.sass'] = 10 | function(module, filename) { 11 | return module._compile("", filename); 12 | }; 13 | 14 | require.extensions['.html'] = 15 | require.extensions['.md'] = 16 | function(module, filename) { 17 | const text = fs.readFileSync(filename, { encoding: 'utf-8' }); 18 | return module._compile("module.exports = " + JSON.stringify(text), filename); 19 | }; 20 | 21 | module.exports = { 22 | parseCliArgs, 23 | run, 24 | defaultOptions, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/react-server.js: -------------------------------------------------------------------------------- 1 | import callerDependency from "./callerDependency"; 2 | 3 | // We need react-server to be a singleton, and we want our caller's copy. 4 | module.exports = require(callerDependency("react-server")); 5 | -------------------------------------------------------------------------------- /packages/react-server-cli/src/setupLogging.js: -------------------------------------------------------------------------------- 1 | import reactServer from "./react-server"; 2 | 3 | export default function setupLogging({logLevel, timingLogLevel, gaugeLogLevel}){ 4 | reactServer.logging.setLevel("main", logLevel); 5 | reactServer.logging.setLevel("time", timingLogLevel); 6 | reactServer.logging.setLevel("gauge", gaugeLogLevel); 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/BasicPage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class TransitionPage { 4 | getElements() { 5 | return React.createElement('h1', null, 'Yay!'); 6 | } 7 | } 8 | 9 | module.exports = TransitionPage; 10 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-basic/in-files/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "middleware": [], 3 | "routes": { 4 | "one": { 5 | "path": "/", 6 | "page": "../../../BasicPage.js" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-basic/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": ["start", "--port", "8780"], 3 | "stdoutIncludes": "Ready for requests on 0.0.0.0:8780" 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-missing-routes/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": ["start", "--port", "8781"], 3 | "stderrIncludes": "Failed to load routes file at" 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-reactserverrc/in-files/.reactserverrc: -------------------------------------------------------------------------------- 1 | { 2 | "routesFile": "customRoutes.js", 3 | "port": 4000 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-reactserverrc/in-files/customRoutes.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | routes: { 3 | "one": { 4 | "path": "/", 5 | "page": "../../../BasicPage.js", 6 | }, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-reactserverrc/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": ["start", "--port", "8783"], 3 | "stdoutIncludes": "Ready for requests on 0.0.0.0:8783" 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-routes-with-error/in-files/routes.js: -------------------------------------------------------------------------------- 1 | undeclaredVariable(); // eslint-disable-line no-undef 2 | -------------------------------------------------------------------------------- /packages/react-server-cli/test/fixtures/commands/start-routes-with-error/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "args": ["start", "--routes-file", "routes.js", "--port", "8784"], 3 | "stderrIncludes": "ReferenceError: undeclaredVariable is not defined" 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/README.md: -------------------------------------------------------------------------------- 1 | Core page middleware used by React Server's CLI 2 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | coreJsMiddleware: require('./lib/coreJsMiddleware').default, 3 | coreCssMiddleware: require('./lib/coreCssMiddleware').default, 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-server-core-middleware", 3 | "version": "1.0.0-alpha.2", 4 | "description": "Core page middleware for React Server", 5 | "main": "index.js", 6 | "scripts": { 7 | "prepublish": "npm run build", 8 | "build": "babel --root-mode upward src -d lib", 9 | "clean": "rimraf lib", 10 | "lint": "eslint src" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/redfin/react-server.git" 15 | }, 16 | "keywords": [ 17 | "React", 18 | "Server", 19 | "middleware" 20 | ], 21 | "author": "", 22 | "license": "Apache-2.0", 23 | "bugs": { 24 | "url": "https://github.com/redfin/react-server/issues" 25 | }, 26 | "homepage": "https://github.com/redfin/react-server#readme", 27 | "devDependencies": { 28 | "@babel/cli": "^7.0.0", 29 | "@babel/core": "^7.4.3", 30 | "babel-eslint": "^9.0.0", 31 | "babel-plugin-react-server": "^1.0.0-alpha.2", 32 | "babel-preset-react-server": "^1.0.0-alpha.2", 33 | "rimraf": "^2.6.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/src/coreCssMiddleware.js: -------------------------------------------------------------------------------- 1 | export default (pathToStatic, manifest) => { 2 | return class CoreCssMiddleware { 3 | getHeadStylesheets(next) { 4 | const routeName = this.getRequest().getRouteName(); 5 | const baseUrl = pathToStatic || (typeof window !== "undefined" ? window.__reactServerBase : "/"); 6 | manifest = manifest || (typeof window !== "undefined" ? window.__reactServerManifest: {}); 7 | 8 | if (!manifest || !manifest.cssChunksByName) { 9 | throw new Error("No webpack manifest found while trying to find CSS files."); 10 | } 11 | 12 | const fileNames = []; 13 | 14 | if (manifest.cssChunksByName.common) { 15 | fileNames.push(baseUrl + manifest.cssChunksByName.common); 16 | } 17 | 18 | if (manifest.cssChunksByName[routeName]) { 19 | fileNames.push(baseUrl + manifest.cssChunksByName[routeName]); 20 | } 21 | 22 | return [ 23 | ...fileNames, 24 | ...next(), 25 | ]; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/react-server-core-middleware/src/coreJsMiddleware.js: -------------------------------------------------------------------------------- 1 | export default (pathToStatic, manifest) => { 2 | return class CoreJsMiddleware { 3 | getSystemScripts(next) { 4 | const routeName = this.getRequest().getRouteName(); 5 | const baseUrl = pathToStatic || "/"; 6 | 7 | const scripts = []; 8 | 9 | // read out the chunkmap so that the client code knows how to download the 10 | // other chunks. 11 | scripts.push({ 12 | type: "text/javascript", 13 | text: `window.webpackManifest = ${JSON.stringify(manifest.jsChunksById)};`, 14 | }); 15 | 16 | if (manifest.jsChunksByName.common) { 17 | scripts.push(`${baseUrl}${manifest.jsChunksByName.common}`); 18 | } 19 | 20 | scripts.push(`${baseUrl}${manifest.jsChunksByName[routeName]}`); 21 | 22 | return [ 23 | ...scripts, 24 | { 25 | type: "text/javascript", 26 | text: ` 27 | if (typeof window !== "undefined") { 28 | if (window.__setReactServerBase) { 29 | window.__setReactServerBase(${JSON.stringify(baseUrl)}); 30 | } 31 | window.__reactServerManifest = ${manifest ? JSON.stringify(manifest) : "{}"}; 32 | } 33 | `, 34 | }, 35 | ...next(), 36 | ]; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | /index.js 3 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | gulpfile.js 3 | main.js 4 | .* # dot files 5 | npm-debug.log* 6 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const babel = require('gulp-babel'); 3 | const rename = require('gulp-rename'); 4 | const tagger = require('react-server-gulp-module-tagger'); 5 | 6 | const SRC = "src/**/*.js"; 7 | 8 | gulp.task('build', ['build-index', 'build-lib']); 9 | 10 | gulp.task('build-index', () => gulp.src('main.js') 11 | .pipe(tagger()) 12 | .pipe(rename('index.js')) 13 | .pipe(gulp.dest(".")) 14 | ) 15 | 16 | gulp.task('build-lib', () => gulp.src(SRC) 17 | .pipe(babel({ 18 | rootMode: "upward", 19 | })) 20 | .pipe(gulp.dest("./lib")) 21 | ); 22 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/main.js: -------------------------------------------------------------------------------- 1 | require("./lib/logging").setLogger( 2 | require("react-server").logging.getLogger(__LOGGER__) 3 | ); 4 | 5 | module.exports = require("./lib/index.js"); 6 | module.exports.default = require("./lib/index.js"); 7 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-server-data-bundle-cache", 3 | "version": "1.0.0-alpha.2", 4 | "description": "React Server Data Bundle Cache", 5 | "main": "index.js", 6 | "repository": "redfin/react-server", 7 | "author": "Bo Borgerson", 8 | "license": "Apache License 2.0", 9 | "scripts": { 10 | "build": "gulp build", 11 | "clean": "rimraf lib npm-debug.log* index.js", 12 | "lint": "eslint src", 13 | "prepublish": "npm run build" 14 | }, 15 | "dependencies": { 16 | "lodash": "^4.16.4", 17 | "stratocacher": "0.0.12", 18 | "stratocacher-layer-lru": "0.0.12" 19 | }, 20 | "peerDependencies": { 21 | "react-server": "^1.0.0-alpha.2" 22 | }, 23 | "devDependencies": { 24 | "react-server": "^1.0.0-alpha.2", 25 | "react-server-gulp-module-tagger": "^1.0.0-alpha.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/before.js: -------------------------------------------------------------------------------- 1 | import lastUrl from "./last-url"; 2 | import listen from "./listen"; 3 | 4 | export default function before([url]) { 5 | listen(); 6 | lastUrl(url); 7 | return [url]; 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/constants.js: -------------------------------------------------------------------------------- 1 | import { 2 | DEFAULT_TTL, 3 | } from "stratocacher/lib/constants"; 4 | 5 | // These are the public options. 6 | export const WRAP_OPTS = ['ttl', 'ttr'] 7 | export const LRU_OPTS = ['max'] 8 | 9 | export const OVERRIDEABLE_OPTS = Object.freeze({ 10 | ttl : DEFAULT_TTL, 11 | max : Infinity, 12 | }); 13 | 14 | export const CACHE_NAME = 'react-server-data-bundle-cache'; 15 | 16 | export { DEFAULT_TTL }; 17 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/fetch.js: -------------------------------------------------------------------------------- 1 | let _fetch; 2 | export default function(fetch) { 3 | if (fetch) { 4 | _fetch = fetch; 5 | } 6 | return _fetch; 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/index.js: -------------------------------------------------------------------------------- 1 | import install from "./install"; 2 | import optIn from "./opt-in"; 3 | import preload from "./preload"; 4 | 5 | export {install, optIn, preload}; 6 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/install.js: -------------------------------------------------------------------------------- 1 | import wrap from "./wrap"; 2 | import logEvents from "./log-events"; 3 | 4 | let _didInstall = 0; 5 | 6 | export default function(opts) { 7 | 8 | // Only going to wrap once. 9 | if (_didInstall++) return; 10 | 11 | wrap(opts); 12 | 13 | logEvents(); 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/last-url.js: -------------------------------------------------------------------------------- 1 | let _url; 2 | export default function lastUrl (url) { 3 | if (url) _url = url; 4 | return _url; 5 | } 6 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/listen.js: -------------------------------------------------------------------------------- 1 | import {getCurrentRequestContext} from "react-server"; 2 | import fetch from "./fetch"; 3 | import {reset, canCache} from "./opt-in"; 4 | import lastUrl from "./last-url"; 5 | 6 | let didListen = 0; 7 | export default function(){ 8 | if (didListen++) return; 9 | 10 | const ctx = getCurrentRequestContext(); 11 | 12 | ctx.onNavigateStart(reset); 13 | ctx.onLoadComplete(() => canCache() || fetch().invalidate(lastUrl())); 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/log-events.js: -------------------------------------------------------------------------------- 1 | import {events} from "stratocacher"; 2 | import {CACHE_NAME} from "./constants"; 3 | import {getLogger} from "./logging"; 4 | 5 | const logger = getLogger(); 6 | 7 | export default function logEvents() { 8 | events.on('error', err => logger.error(err)); 9 | events.on('time', ({name, type, time}) => { 10 | if (name === CACHE_NAME) { 11 | logger.time(`stats.${type}`, time); 12 | } 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/logging.js: -------------------------------------------------------------------------------- 1 | let _logger; 2 | export function setLogger(logger) { 3 | _logger = logger; 4 | } 5 | export function getLogger() { 6 | return _logger; 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/opt-in.js: -------------------------------------------------------------------------------- 1 | let _canCache; 2 | export function reset () { _canCache = false } 3 | export default function optIn () { _canCache = true } 4 | export function canCache () { return _canCache } 5 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/preload.js: -------------------------------------------------------------------------------- 1 | import fetch from "./fetch"; 2 | 3 | export default function preload(url) { 4 | return fetch()(url); 5 | } 6 | -------------------------------------------------------------------------------- /packages/react-server-data-bundle-cache/src/wrap.js: -------------------------------------------------------------------------------- 1 | import _ from "lodash"; 2 | import {wrap} from "stratocacher"; 3 | import LayerLRU from "stratocacher-layer-lru"; 4 | import {ReactServerAgent} from "react-server"; 5 | import {CACHE_NAME, WRAP_OPTS, LRU_OPTS, OVERRIDEABLE_OPTS} from "./constants"; 6 | import fetch from "./fetch"; 7 | import before from "./before"; 8 | 9 | function getOpts(opts, keys) { 10 | return _.pick(_.assign({}, OVERRIDEABLE_OPTS, opts), keys); 11 | } 12 | 13 | export default function(opts) { 14 | 15 | const INTERNAL_OPTS = { 16 | name: CACHE_NAME, 17 | layers: [ LayerLRU.configure(getOpts(opts, LRU_OPTS)) ], 18 | before, 19 | } 20 | 21 | fetch(ReactServerAgent._fetchDataBundle = wrap( 22 | _.assign(getOpts(opts, WRAP_OPTS), INTERNAL_OPTS), 23 | ReactServerAgent._fetchDataBundle 24 | )); 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-server-examples/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-server"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-server-examples/.eslintignore: -------------------------------------------------------------------------------- 1 | **/__clientTemp/** 2 | -------------------------------------------------------------------------------- /packages/react-server-examples/README.md: -------------------------------------------------------------------------------- 1 | # react-server-examples 2 | 3 | Some example apps for `react-server`, which use `react-server-cli` to compile the code and run the server. 4 | 5 | Installation and running instructions are in the respective README.md files: 6 | 7 | - [**hello-world**](./hello-world): A simple one-page app that shows off server rendering, a simple routes file, and client-side interactivity. 8 | - [**bike-share**](./bike-share): An example project for react-server which demos server rendering, interactivity on the client side, ReactServerAgent, url parameters and logging. 9 | - [**redux-basic**](./redux-basic): An example of the official Redux Counter app powered by react-server. 10 | - [**meteor-site**](./meteor-site): An example of react-server using Google Map, redux and transitions. 11 | - [**code-splitting**](./code-splitting): An example showing off how to do code splitting in react-server. 12 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-server"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | __clientTemp 4 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/.reactserverrc: -------------------------------------------------------------------------------- 1 | { 2 | "routesFile": "./routes.js", 3 | "customMiddlewarePath": "./api" 4 | } 5 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:slim 2 | 3 | EXPOSE 3000 4 | ENV NODE_ENV=docker-dev 5 | VOLUME /www -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/api/index.js: -------------------------------------------------------------------------------- 1 | import { getNetworks, getNetwork } from "./networks"; 2 | 3 | function setJsonContentType(req, res, next) { 4 | res.type('json'); 5 | next(); 6 | } 7 | 8 | export default function registerCustomMiddlewares(server, registerRenderMiddleware) { 9 | server.use('/api', setJsonContentType); 10 | server.get('/api/networks', getNetworks); 11 | server.get('/api/networks/:network', getNetwork); 12 | registerRenderMiddleware(); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/api/networks.js: -------------------------------------------------------------------------------- 1 | import request from "request-promise"; 2 | import {logging} from 'react-server'; 3 | 4 | const logger = logging.getLogger(__LOGGER__); 5 | const citybikesApi = 'http://api.citybik.es/v2/networks'; 6 | 7 | export function getNetworks(req, res) { 8 | const url = citybikesApi; 9 | 10 | logger.info(`requesting ${url}`); 11 | request(url) 12 | .then(data => { 13 | logger.info(`got data ${data} from url ${url}`); 14 | res.status(200).send(data); 15 | }) 16 | .catch(err => { 17 | res.status(500).send(err); 18 | }); 19 | } 20 | 21 | export function getNetwork(req, res) { 22 | const network = req.params.network; 23 | const url = `${citybikesApi}/${network}`; 24 | 25 | logger.info(`requesting ${url}`); 26 | request(url) 27 | .then(data => { 28 | logger.info(`got data ${data} from url ${url}`); 29 | res.status(200).send(data); 30 | }) 31 | .catch(err => { 32 | res.status(500).send(err); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Error = () => { 4 | return (
404 - The page you were looking for was not found.
); 5 | }; 6 | 7 | export default Error; 8 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {logging} from 'react-server'; 3 | 4 | const logger = logging.getLogger(__LOGGER__); 5 | 6 | const Footer = () => { 7 | logger.info('rendering the footer'); 8 | return (
9 | Brought to you by 10 | React Server 11 | and 12 | citybik.es 13 |
); 14 | }; 15 | 16 | export default Footer; 17 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/header.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {logging} from 'react-server'; 3 | 4 | const logger = logging.getLogger(__LOGGER__); 5 | 6 | const Header = () => { 7 | logger.info('rendering the header'); 8 | return (

React Server city bikes page

); 9 | }; 10 | 11 | export default Header; 12 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/network-card.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import {logging, Link} from 'react-server'; 4 | 5 | const logger = logging.getLogger(__LOGGER__); 6 | 7 | const NetworkCard = ({id, name, location, company}) => { 8 | logger.info(`rendering card for network ${name}`); 9 | return ( 10 |
{name} in {location.city}, {location.country}, run by {company}
11 | ); 12 | }; 13 | 14 | NetworkCard.propTypes = { 15 | company: PropTypes.any, 16 | href: PropTypes.string, 17 | id: PropTypes.string, 18 | location: PropTypes.shape({ 19 | city: PropTypes.string, 20 | country: PropTypes.string, 21 | latitude: PropTypes.number, 22 | longitude: PropTypes.number, 23 | }), 24 | name: PropTypes.string, 25 | stations: PropTypes.array, 26 | }; 27 | 28 | NetworkCard.displayName = 'NetworkCard'; 29 | 30 | export default NetworkCard; 31 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/network-list.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import {logging} from 'react-server'; 4 | import NetworkCard from './network-card'; 5 | 6 | const logger = logging.getLogger(__LOGGER__); 7 | 8 | const NetworkList = ({networks}) => { 9 | logger.info(`rendering list of ${networks.length} networks`); 10 | const networkCards = networks.map(network => { 11 | return ; 12 | }); 13 | return
{networkCards}
; 14 | }; 15 | 16 | NetworkList.propTypes = { 17 | networks: PropTypes.array, 18 | }; 19 | 20 | NetworkList.displayName = 'NetworkList'; 21 | 22 | export default NetworkList; 23 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/station-card.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import {logging} from 'react-server'; 4 | 5 | const logger = logging.getLogger(__LOGGER__); 6 | const timeSinceTimestamp = s => { 7 | const parsed = Date.parse(s); 8 | const timeSince = (new Date()) - parsed; 9 | const minutesSince = Math.floor(timeSince / 60000); 10 | const secondsSince = Math.floor((timeSince / 1000) % 60); 11 | return `${minutesSince} min, ${secondsSince} sec`; 12 | }; 13 | 14 | const StationCard = ({station}) => { 15 | logger.info(`rendering card for station ${JSON.stringify(station)}`); 16 | return ( 17 |
{station.name} had {station.empty_slots} empty slots {timeSinceTimestamp(station.timestamp)} ago.
18 | ); 19 | }; 20 | 21 | StationCard.propTypes = { 22 | station: PropTypes.shape({ 23 | empty_slots: PropTypes.number, // eslint-disable-line 24 | extra: PropTypes.object, 25 | free_bikes: PropTypes.number, // eslint-disable-line 26 | id: PropTypes.string, 27 | latitude: PropTypes.number, 28 | longitude: PropTypes.number, 29 | name: PropTypes.string, 30 | timestamp: PropTypes.string, 31 | }), 32 | }; 33 | 34 | StationCard.displayName = 'StationCard'; 35 | 36 | export default StationCard; 37 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/components/station-list.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React from 'react'; 3 | import {logging} from 'react-server'; 4 | import StationCard from './station-card'; 5 | 6 | const logger = logging.getLogger(__LOGGER__); 7 | 8 | const StationList = ({stations}) => { 9 | logger.info(`rendering list of ${stations.length} stations`); 10 | const stationCards = stations.map(station => ); 11 | return
{stationCards}
; 12 | }; 13 | 14 | StationList.propTypes = { 15 | stations: PropTypes.array, 16 | }; 17 | 18 | StationList.displayName = 'StationList'; 19 | 20 | export default StationList; 21 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | react_server: # The label of the service 4 | build: . # The location of the Dockerfile to build 5 | volumes: # Files to share with the container and the host 6 | - .:/www # Share the project in /www in the container 7 | - react_server_node_modules:/www/node_modules # A special volume so that OS specific node_modules are built correctly 8 | working_dir: /www # The location all commands should run from 9 | environment: 10 | NODE_ENV: 'docker' # Set the NODE_ENV environment variable 11 | command: /bin/bash -c "npm install && npm start" # The command to run in when the container starts 12 | ports: # Ports to expose to your host 13 | - '3000:3000' 14 | - '3001:3001' 15 | # An example database 16 | # my_db: 17 | # image: rethinkdb:latest 18 | # You can reference the db/service by using the dns name `my_db` and docker will do the rest 19 | 20 | # Volume to store separate from the container runtime and host 21 | volumes: 22 | react_server_node_modules: -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/middleware/request-to-port.js: -------------------------------------------------------------------------------- 1 | import {ReactServerAgent} from 'react-server'; 2 | export default class RequestToPortMiddleware { 3 | handleRoute(next) { 4 | if (typeof window === 'undefined') { //eslint-disable-line 5 | ReactServerAgent.plugRequest(req => { 6 | req.urlPrefix('localhost:3000'); 7 | }); 8 | } 9 | return next(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-server-bike-share", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "A react-server instance", 6 | "main": "HelloWorld.js", 7 | "author": "Doug Wade ", 8 | "scripts": { 9 | "clean": "rm -rf build __clientTemp", 10 | "start": "npm run clean && react-server start --hot" 11 | }, 12 | "license": "Apache-2.0", 13 | "dependencies": { 14 | "react": "^15.4.1", 15 | "react-dom": "15.4.1", 16 | "react-server": "^0.6.3", 17 | "react-server-cli": "^0.6.3", 18 | "request-promise": "^4.1.1", 19 | "superagent": "1.8.4" 20 | }, 21 | "devDependencies": { 22 | "babel-preset-react-server": "^0.4.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-server-examples/bike-share/pages/not-found.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {RootElement} from 'react-server'; 3 | 4 | import Header from '../components/header'; 5 | import Footer from '../components/footer'; 6 | import Error from '../components/error'; 7 | 8 | import '../styles/index.scss'; 9 | 10 | export default class NotFoundPage { 11 | handleRoute () { 12 | return {code: 404, hasDocument: true}; 13 | } 14 | 15 | getElements () { 16 | return [ 17 | 18 |
19 | , 20 | 21 | 22 | , 23 | 24 |