- Bumps Node to v12.2
- Fixes `Dockerfile`
- Replaces MobX with MobX React Lite - compatible with hooks!
- Replaces `ts-loader` with Babel 7 Typescript preset
- Refactors `tsconfig.json` and imports to use module interop (e.g. no more `import * as...`)
- Removes `src/data` folder; routes are now defined directly in components, and store data is added via MobX in React hooks

- Adds NPM packages:
"@babel/plugin-proposal-class-properties": "^7.4.4"
"@babel/plugin-proposal-object-rest-spread": "^7.4.4"
"@babel/preset-react": "^7.0.0"
"@babel/preset-typescript": "^7.3.3"
"mobx-react-lite": "^1.3.2"

- Bumps NPM packages:
"mobx": "^5.9.4"
pull/164/head
Lee Benson 4 years ago
parent 0989b32674
commit 03f8760ff2
  1. 6
      Dockerfile
  2. 22
      README.md
  3. 266
      package-lock.json
  4. 13
      package.json
  5. 27
      src/components/example/count.tsx
  6. 4
      src/components/example/dynamic.tsx
  7. 2
      src/components/example/hackernews.tsx
  8. 2
      src/components/example/index.tsx
  9. 2
      src/components/helpers/scrollTop.tsx
  10. 14
      src/components/root.tsx
  11. 34
      src/data/routes.ts
  12. 11
      src/data/store.ts
  13. 31
      src/entry/client.tsx
  14. 35
      src/entry/server.tsx
  15. 6
      src/lib/apollo.ts
  16. 12
      src/lib/hotServerMiddleware.ts
  17. 2
      src/lib/stats.ts
  18. 55
      src/lib/store.ts
  19. 18
      src/runner/app.ts
  20. 4
      src/runner/build.ts
  21. 4
      src/runner/development.ts
  22. 6
      src/runner/production.ts
  23. 6
      src/runner/static.ts
  24. 2
      src/views/ssr.tsx
  25. 8
      src/webpack/client.ts
  26. 22
      src/webpack/common.ts
  27. 2
      src/webpack/css.ts
  28. 4
      src/webpack/server.ts
  29. 4
      src/webpack/static.ts
  30. 40
      tsconfig.json
  31. 1
      types/fonts.d.ts
  32. 1
      types/global.d.ts
  33. 1
      types/images.d.ts

@ -1,4 +1,4 @@
FROM node:11.8.0-alpine AS builder
FROM node:12.2.0-alpine AS builder
# log most things
ENV NPM_CONFIG_LOGLEVEL notice
@ -15,11 +15,11 @@ RUN npm i
ADD . .
# build
RUN npm run build
RUN npm run build:production
########################
FROM node:11.8.0-alpine
FROM node:12.2.0-alpine
WORKDIR /app
# copy source + compiled `node_modules`

@ -11,13 +11,13 @@ https://reactql.org
### Front-end stack
- [React v16](https://facebook.github.io/react/) for UI.
- [Apollo Client 2.0 (React)](http://dev.apollodata.com/react/) for connecting to GraphQL.
- [MobX](https://mobx.js.org/) for declarative, type-safe flux/store state management (automatically re-hydrated from the server.) which is auto-saved and reloaded to `localStorage` in the client (simple to disable if you don't need it.)
- [React v16.8](https://facebook.github.io/react/) (the one with [hooks](https://reactjs.org/docs/hooks-intro.html)!) for UI.
- [Apollo Client 2.5 (React)](http://dev.apollodata.com/react/) for connecting to GraphQL.
- [MobX-React-Lite](https://github.com/mobxjs/mobx-react-lite) for declarative, type-safe flux/store state management.
- [Emotion](https://emotion.sh/) CSS-in-JS, with inline `<style>` tag generation that contains only the CSS that needs to be rendered.
- [Sass](https://sass-lang.com/), [Less](http://lesscss.org/) and [PostCSS](https://postcss.org/) when importing `.css/.scss/.less` files.
- [React Router 4](https://reacttraining.com/react-router/) for declarative browser + server routes.
- [GraphQL Code Generator v1](https://graphql-code-generator.com/) for parsing remote GraphQL server schemas, for automatically building fully-typed Apollo React HOCs instead of writing `<Query>` / `<Mutation>` queries manually
- [GraphQL Code Generator v1.1](https://graphql-code-generator.com/) for parsing remote GraphQL server schemas, for automatically building fully-typed Apollo React HOCs instead of writing `<Query>` / `<Mutation>` queries manually
- Declarative/dynamic `<head>` section, using [react-helmet](https://github.com/nfl/react-helmet).
### Server-side rendering
@ -26,7 +26,7 @@ https://reactql.org
- Full route-aware server-side rendering (SSR) of initial HTML.
- Universal building - both browser + Node.js web server compile down to static files, for fast server re-spawning.
- Per-request GraphQL store. Store state is dehydrated via SSR, and rehydrated automatically on the client.
- MobX for app-wide flux/store state, with a built-in `<StateConsumer>` for automatically re-rendering any React component that 'listens' to state and full client-side rehydration. Fully typed state!
- MobX for app-wide flux/store state, for automatically re-rendering any React component that 'listens' to state. Fully typed state!
- Full page React via built-in SSR component - every byte of your HTML is React.
- SSR in both development and production, even with hot-code reload.
@ -214,17 +214,15 @@ The important stuff is in [src](src).
Here's a quick run-through of each sub-folder and what you'll find in it:
- [src/components](src/components) - React components. Follow the import flow at [root.tsx](src/components/root.tsx) to figure out the component render chain. I've included an [example](src/components/example) component that shows off some Apollo GraphQL and MobX features, including incrementing a local counter and pulling top news stories from Hacker News (a live GraphQL server endpoint.)
- [src/data](src/data) - Data used throughout your app. You'll find [routes.ts](src/data/routes.ts), which defines your React Router routes (currently, just the home page -- but you can easily extend this.) and [state.ts](src/data/state.ts), to show you how simple it is to define your own state data fields that, when modified, automatically re-render any 'observing' component.
- [src/components](src/components) - React components. Follow the import flow at [root.tsx](src/components/root.tsx) to figure out the component render chain and routing. I've included an [example](src/components/example) component that shows off some Apollo GraphQL and MobX features, including incrementing a local counter and pulling top news stories from Hacker News (a live GraphQL server endpoint.)
- [src/entry](src/entry) - The client and server entry points, which call on [src/components/root.tsx](src/components/root.tsx) to isomorphically render the React chain in both environments.
- [src/global](src/global) - A good place for anything that's used through your entire app, like global styles. I've started you off with a [styles.ts](src/global/styles.ts) that sets globally inlined Emotion CSS, as well as pulls in a global `.scss` file -- to show you how both types of CSS work.
- [src/lib](src/lib) - Internal libraries/helpers. There's an [apollo.ts](src/lib/apollo.ts) which builds a universal Apollo Client, and [mobx.ts](src/lib/mobx.tsx) which sets up default state (automatically rehydrated on the client), for incrementing a local counter. Plus, Koa middleware to handle hot-code reloading in development and some other Webpack helpers.
- [src/lib](src/lib) - Internal libraries/helpers. There's an [apollo.ts](src/lib/apollo.ts) which builds a universal Apollo Client. Plus, Koa middleware to handle hot-code reloading in development and some other Webpack helpers.
- [src/queries](src/queries) - Your GraphQL queries. There's just one by default - for pilling the top stories from Hacker News to display in the example component.
- [src/queries](src/queries) - Your GraphQL queries. There's just one by default - for pulling the top stories from Hacker News to display in the example component.
- [src/runner](src/runner) - Development and production runners that spawn the Webpack build process in each environment.
@ -236,13 +234,13 @@ You'll also find some other useful goodies in the [root]()...
- [.env](.env) - Change your `GRAPHQL` server endpoint, `WS_SUBSCRIPTIONS=1` for built-in WebSocket support, `HOST` if you want to bind the server to something other than localhost, and `LOCAL_STORAGE_KEY` to set the root key for saving MobX state locally in the client for automatic re-loading in a later session.
- [.nvmrc](.nvmrc) - Specify your preferred Node.js version, for use with NVM and used by many continuous deployment tools. Defaults to v11.8.0
- [.nvmrc](.nvmrc) - Specify your preferred Node.js version, for use with NVM and used by many continuous deployment tools. Defaults to v12.2.0
- [codegen.yml](codegen.yml) - Settings for [GraphQL Code Generator](https://graphql-code-generator.com/) (which you can run with `npm run gen:graphql` to generate types/HOCs based on your GraphQL queries/mutations.)
- [netlify.toml](netlify.toml) - Build instructions for fast [Netlify](https://www.netlify.com/) deployments. **Tip: To quickly deploy a demo ReactQL app, [click here](https://app.netlify.com/start/deploy?repository=https://github.com/leebenson/reactql).**
- [types](types) - Some basic types that allow you to import fonts, images, CSS/SASS/LESS files, and allow use of the global `SERVER` boolean in your IDE.
- [types](types) - Some basic types that allow you to import fonts, images, CSS/SASS/LESS files, and allow use of the global `SERVER` boolean and `GRAPHQL` endpoint data in your IDE.
- Typescript configuration via [tsconfig.json](tsconfig.json)

266
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "reactql",
"version": "4.3.1",
"version": "4.5.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -104,6 +104,30 @@
}
}
},
"@babel/helper-builder-react-jsx": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz",
"integrity": "sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==",
"dev": true,
"requires": {
"@babel/types": "^7.3.0",
"esutils": "^2.0.0"
}
},
"@babel/helper-create-class-features-plugin": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz",
"integrity": "sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA==",
"dev": true,
"requires": {
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-replace-supers": "^7.4.4",
"@babel/helper-split-export-declaration": "^7.4.4"
}
},
"@babel/helper-function-name": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
@ -124,6 +148,15 @@
"@babel/types": "^7.0.0"
}
},
"@babel/helper-member-expression-to-functions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz",
"integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==",
"dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
},
"@babel/helper-module-imports": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
@ -132,12 +165,46 @@
"@babel/types": "^7.0.0"
}
},
"@babel/helper-optimise-call-expression": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz",
"integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==",
"dev": true,
"requires": {
"@babel/types": "^7.0.0"
}
},
"@babel/helper-plugin-utils": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
"integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
"dev": true
},
"@babel/helper-replace-supers": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz",
"integrity": "sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/traverse": "^7.4.4",
"@babel/types": "^7.4.4"
},
"dependencies": {
"@babel/types": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
"integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.11",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/helper-split-export-declaration": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz",
@ -201,6 +268,26 @@
"integrity": "sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w==",
"dev": true
},
"@babel/plugin-proposal-class-properties": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.4.tgz",
"integrity": "sha512-WjKTI8g8d5w1Bc9zgwSz2nfrsNQsXcCf9J9cdCvrJV6RF56yztwm4TmJC0MgJ9tvwO9gUA/mcYe89bLdGfiXFg==",
"dev": true,
"requires": {
"@babel/helper-create-class-features-plugin": "^7.4.4",
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-proposal-object-rest-spread": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz",
"integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-object-rest-spread": "^7.2.0"
}
},
"@babel/plugin-syntax-dynamic-import": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz",
@ -210,6 +297,106 @@
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-syntax-jsx": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz",
"integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-syntax-object-rest-spread": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz",
"integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-syntax-typescript": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz",
"integrity": "sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-transform-react-display-name": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz",
"integrity": "sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-transform-react-jsx": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz",
"integrity": "sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==",
"dev": true,
"requires": {
"@babel/helper-builder-react-jsx": "^7.3.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-jsx": "^7.2.0"
}
},
"@babel/plugin-transform-react-jsx-self": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz",
"integrity": "sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-jsx": "^7.2.0"
}
},
"@babel/plugin-transform-react-jsx-source": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz",
"integrity": "sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-jsx": "^7.2.0"
}
},
"@babel/plugin-transform-typescript": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.4.tgz",
"integrity": "sha512-rwDvjaMTx09WC0rXGBRlYSSkEHOKRrecY6hEr3SVIPKII8DVWXtapNAfAyMC0dovuO+zYArcAuKeu3q9DNRfzA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-syntax-typescript": "^7.2.0"
}
},
"@babel/preset-react": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz",
"integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-transform-react-display-name": "^7.0.0",
"@babel/plugin-transform-react-jsx": "^7.0.0",
"@babel/plugin-transform-react-jsx-self": "^7.0.0",
"@babel/plugin-transform-react-jsx-source": "^7.0.0"
}
},
"@babel/preset-typescript": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz",
"integrity": "sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-transform-typescript": "^7.3.2"
}
},
"@babel/runtime": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.4.tgz",
@ -2094,15 +2281,23 @@
}
},
"babel-loader": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz",
"integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==",
"version": "8.0.6",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.6.tgz",
"integrity": "sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw==",
"dev": true,
"requires": {
"find-cache-dir": "^2.0.0",
"loader-utils": "^1.0.2",
"mkdirp": "^0.5.1",
"util.promisify": "^1.0.0"
"pify": "^4.0.1"
},
"dependencies": {
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
}
}
},
"babel-messages": {
@ -2161,9 +2356,9 @@
}
},
"js-yaml": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz",
"integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==",
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@ -6129,9 +6324,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
@ -7234,18 +7429,14 @@
}
},
"mobx": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.9.2.tgz",
"integrity": "sha512-dMGeM4emHDTaXEA/Va4NhWSiEk8XkdcFQl4uiictXoou0Vbk0Sup4H/HDWGYcXVeRAWGPDYXDDkz1gXK4B1eZw=="
"version": "5.9.4",
"resolved": "https://registry.npmjs.org/mobx/-/mobx-5.9.4.tgz",
"integrity": "sha512-L9JjTX2rtQUAhCIgnHokfntNOsF14uioT9LqStf6Mya+16j56ZBe21E8Y9V59tfr2aH2kLQPD10qtCJXBuTAxw=="
},
"mobx-react": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.3.tgz",
"integrity": "sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ==",
"requires": {
"hoist-non-react-statics": "^3.0.0",
"react-lifecycles-compat": "^3.0.2"
}
"mobx-react-lite": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-1.3.2.tgz",
"integrity": "sha512-D8VZEsxSxMNYDJmw2SgUXVAgpOlVYmp3hBCusoe+LuBDYUqyn2K3RPrMEF0rIzYJAvqpyv7YRrH8I3eBsPPx1A=="
},
"move-concurrently": {
"version": "1.0.1",
@ -11075,37 +11266,6 @@
"tslib": "^1.9.3"
}
},
"ts-loader": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.0.0.tgz",
"integrity": "sha512-lszy+D41R0Te2+loZxADWS+E1+Z55A+i3dFfFie1AZHL++65JRKVDBPQgeWgRrlv5tbxdU3zOtXp8b7AFR6KEg==",
"dev": true,
"requires": {
"chalk": "^2.3.0",
"enhanced-resolve": "^4.0.0",
"loader-utils": "^1.0.2",
"micromatch": "^4.0.0",
"semver": "^6.0.0"
},
"dependencies": {
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"semver": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz",
"integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==",
"dev": true
}
}
},
"ts-log": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.1.4.tgz",

@ -1,6 +1,6 @@
{
"name": "reactql",
"version": "4.3.1",
"version": "4.5.0",
"description": "ReactQL - front-end React/GraphQL starter kit",
"main": "index.js",
"scripts": {
@ -33,7 +33,11 @@
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-object-rest-spread": "^7.4.4",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@graphql-codegen/cli": "^1.1.3",
"@graphql-codegen/fragment-matcher": "^1.1.3",
"@graphql-codegen/typescript": "^1.1.3",
@ -65,7 +69,7 @@
"@types/webpack-node-externals": "^1.6.3",
"@types/ws": "^6.0.1",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.5",
"babel-loader": "^8.0.6",
"babel-plugin-emotion": "^10.0.9",
"brotli-webpack-plugin": "^1.1.0",
"compression-webpack-plugin": "^2.0.0",
@ -89,7 +93,6 @@
"rimraf": "^2.6.3",
"sass-loader": "^7.1.0",
"source-map-support": "^0.5.12",
"ts-loader": "^6.0.0",
"ts-node": "^8.1.0",
"tslint": "^5.16.0",
"typescript": "^3.4.5",
@ -121,8 +124,8 @@
"koa-send": "^5.0.0",
"lodash": "^4.17.11",
"microseconds": "^0.1.0",
"mobx": "^4.9.2",
"mobx-react": "^5.4.3",
"mobx": "^5.9.4",
"mobx-react-lite": "^1.3.2",
"ora": "^3.4.0",
"react": "^16.8.6",
"react-addons-css-transition-group": "^15.6.2",

@ -4,19 +4,20 @@
// IMPORTS
/* NPM */
import * as React from "react";
/* Local */
// `withStore` gives us access to MobX store state
import { withStore } from "@/lib/store";
import React from "react";
import { Observer, useObservable } from "mobx-react-lite";
// ----------------------------------------------------------------------------
export const Count = withStore(({ store }) => (
<>
<h3>Current count (from MobX): {store.count}</h3>
<button onClick={store.increment}>Increment</button>
<button onClick={() => (store.count = 0)}>Reset</button>
</>
));
export const Count: React.FunctionComponent = () => {
const store = useObservable({ count: 0 });
return (
<>
<Observer>
{() => <h3>Current count (from MobX): {store.count}</h3>}
</Observer>
<button onClick={() => store.count++}>Increment</button>
<button onClick={() => (store.count = 0)}>Reset</button>
</>
);
};

@ -5,12 +5,12 @@
/* NPM */
import * as React from "react";
import React from "react";
// ----------------------------------------------------------------------------
// Say hello from GraphQL, along with a HackerNews feed fetched by GraphQL
const Dynamic: React.SFC = () => (
const Dynamic: React.FunctionComponent = () => (
<>
<h2>This component was loaded dynamically!</h2>
</>

@ -4,7 +4,7 @@
// IMPORTS
/* NPM */
import * as React from "react";
import React from "react";
// Emotion styled component
import styled from "@emotion/styled";

@ -5,7 +5,7 @@
/* NPM */
import * as React from "react";
import React from "react";
/* Local */

@ -4,7 +4,7 @@
// IMPORTS
/* NPM */
import * as React from "react";
import React from "react";
import { RouteComponentProps, withRouter } from "react-router-dom";
// -----------------------------------------------------------------------------

@ -4,7 +4,7 @@
// IMPORTS
/* NPM */
import * as React from "react";
import React from "react";
import Helmet from "react-helmet";
import { hot } from "react-hot-loader/root";
import { Route, Switch } from "react-router-dom";
@ -18,12 +18,14 @@ import ScrollTop from "@/components/helpers/scrollTop";
// Global styles
import globalStyles from "@/global/styles";
// Routes
import routes from "@/data/routes";
// By default, pull in the ReactQL example. In your own project, just nix
// the `src/components/example` folder and replace the following line with
// your own React components
import Example from "@/components/example";
// ----------------------------------------------------------------------------
const Root = () => (
const Root: React.FunctionComponent = () => (
<div>
<Global styles={globalStyles} />
<Helmet>
@ -31,9 +33,7 @@ const Root = () => (
</Helmet>
<ScrollTop>
<Switch>
{routes.map(route => (
<Route key={route.path} {...route} />
))}
<Route path="/" exact component={Example} />
</Switch>
</ScrollTop>
</div>

@ -1,34 +0,0 @@
// Routes
// ----------------------------------------------------------------------------
// IMPORTS
/* NPM */
// We're using `react-router-dom` to handle routing, so grab the `RouteProps`
// type that we'll use to ensure our own types conform to the expected configuration
import { RouteProps } from "react-router-dom";
/* Local */
// Components
// By default, pull in the ReactQL example. In your own project, just nix
// the `src/components/example` folder and replace the following line with
// your own React components
import Example from "@/components/example";
// ----------------------------------------------------------------------------
// Specify the routes. This is provided as an array of `RouteProp`, which is
// a type provided by `react-router-dom` for rendering a route. Typically, this
// will contain at least a component and a path
const routes: RouteProps[] = [
{
component: Example, // <-- this is the component that'll be rendered
exact: true, // <-- this says to ONLY match when the path is exactly '/'
path: "/" // <-- ... and this is the actual path to match on
}
];
export default routes;

@ -1,11 +0,0 @@
import { action, observable } from "mobx";
export class Store {
@observable count = 0;
@action public increment = () => {
this.count = this.count + 1;
};
@action public decrement = () => {
--this.count;
};
}

@ -9,20 +9,17 @@
import { createBrowserHistory } from "history";
// React, our UI engine
import * as React from "react";
import React from "react";
// HOC for enabling Apollo GraphQL `<Query>` and `<Mutation>`
import { ApolloProvider } from "react-apollo";
// Attach React to the browser DOM
import * as ReactDOM from "react-dom";
import ReactDOM from "react-dom";
// Single page app routing
import { Router } from "react-router-dom";
// MobX provider
import { Provider } from "mobx-react";
/* Local */
// Our main component, and the starting point for server/browser loading
@ -31,17 +28,10 @@ import Root from "@/components/root";
// Helper function that creates a new Apollo client per request
import { createClient } from "@/lib/apollo";
// MobX state
import { Store } from "@/data/store";
import { rehydrate, autosave } from "@/lib/store";
// ----------------------------------------------------------------------------
// Create new MobX state
const store = ((window as any).store = new Store());
// Create Apollo client
const client = createClient(store);
const client = createClient();
// Create a browser history
const history = createBrowserHistory();
@ -49,15 +39,10 @@ const history = createBrowserHistory();
// Render
const root = document.getElementById("root")!;
ReactDOM[root.innerHTML ? "hydrate" : "render"](
<Provider store={store}>
<ApolloProvider client={client}>
<Router history={history}>
<Root />
</Router>
</ApolloProvider>
</Provider>,
<ApolloProvider client={client}>
<Router history={history}>
<Root />
</Router>
</ApolloProvider>,
document.getElementById("root")
);
// Rehydrate MobX store and save changes
[rehydrate, autosave].forEach(fn => fn(store));

@ -9,7 +9,7 @@
import "cross-fetch/polyfill";
// React for UI
import * as React from "react";
import React from "react";
// The `Context` type for the Koa HTTP server
import { Context } from "koa";
@ -18,10 +18,10 @@ import { Context } from "koa";
import { ApolloProvider, getDataFromTree } from "react-apollo";
// MobX state management
import { toJS } from "mobx";
import { useStaticRendering } from "mobx-react-lite";
// React utility to transform JSX to HTML (to send back to the client)
import * as ReactDOMServer from "react-dom/server";
import ReactDOMServer from "react-dom/server";
// <Helmet> component for retrieving <head> section, so we can set page
// title, meta info, etc along with the initial HTML
@ -30,9 +30,6 @@ import Helmet from "react-helmet";
// React SSR routers
import { StaticRouter } from "react-router";
// MobX provider
import { Provider } from "mobx-react";
/* Local */
// Root component
@ -41,9 +38,6 @@ import Root from "@/components/root";
// Utility for creating a per-request Apollo client
import { createClient } from "@/lib/apollo";
// State class, containing all of our user-land state fields
import { Store } from "@/data/store";
// Class for handling Webpack stats output
import Output from "@/lib/output";
@ -58,16 +52,16 @@ export interface IRouterContext {
url?: string;
}
// Enable SSR-mode with MobX to avoid memory leaks
useStaticRendering(true);
// Everything from this point will be Webpack'd and dumped in `dist/server.js`
// and then loaded into an active Koa server
export default function(output: Output) {
// Create Koa middleware to handle React requests
return async (ctx: Context) => {
// Create new MobX store
const store = new Store();
// Create a new Apollo client
const client = createClient(store);
const client = createClient();
// Create a fresh 'context' for React Router
const routerContext: IRouterContext = {};
@ -75,13 +69,11 @@ export default function(output: Output) {
// Render our components - passing down MobX state, a GraphQL client,
// and a router for rendering based on our route config
const components = (
<Provider store={store}>
<ApolloProvider client={client}>
<StaticRouter location={ctx.request.url} context={routerContext}>
<Root />
</StaticRouter>
</ApolloProvider>
</Provider>
<ApolloProvider client={client}>
<StaticRouter location={ctx.request.url} context={routerContext}>
<Root />
</StaticRouter>
</ApolloProvider>
);
// Await GraphQL data coming from the API server
@ -123,8 +115,7 @@ export default function(output: Output) {
html={html}
scripts={output.client.scripts()}
window={{
__APOLLO__: client.extract(), // <-- GraphQL store
__STORE__: toJS(store) // <-- MobX store
__APOLLO__: client.extract() // <-- GraphQL store
}}
/>
);

@ -18,7 +18,6 @@ import { getMainDefinition } from "apollo-utilities";
import { SubscriptionClient } from "subscriptions-transport-ws";
/* Local */
import { Store } from "@/data/store";
import introspectionQueryResultData from "@/graphql/fragments";
// ----------------------------------------------------------------------------
@ -28,10 +27,7 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
});
export function createClient(
// @ts-ignore - useful to pass in the store for `Authorization` headers, etc
store: Store
): ApolloClient<NormalizedCacheObject> {
export function createClient(): ApolloClient<NormalizedCacheObject> {
// Create the cache first, which we'll share across Apollo tooling.
// This is an in-memory cache. Since we'll be calling `createClient` on
// universally, the cache will survive until the HTTP request is

@ -31,14 +31,14 @@
// IMPORTS
/* Node */
import * as fs from "fs";
import * as path from "path";
import fs from "fs";
import path from "path";
/* NPM */
import * as Koa from "koa";
import * as requireFromString from "require-from-string";
import * as sourceMapSupport from "source-map-support";
import * as webpack from "webpack";
import Koa from "koa";
import requireFromString from "require-from-string";
import sourceMapSupport from "source-map-support";
import webpack from "webpack";
/* Launch.js */
import Output from "./output";

@ -7,7 +7,7 @@
// IMPORTS
/* NPM */
import * as lodash from "lodash";
import lodash from "lodash";
// ----------------------------------------------------------------------------

@ -1,55 +0,0 @@
// MobX helpers
// ----------------------------------------------------------------------------
// IMPORTS
import * as React from "react";
import { runInAction, autorun, toJS } from "mobx";
import { observer, inject } from "mobx-react";
/* Local */
import { Store } from "@/data/store";
// ----------------------------------------------------------------------------
export type WithStore<T> = T & {
store: Store;
};
export function withStore<P>(
Component: React.ComponentType<WithStore<P>>
): React.ComponentType<P> {
return inject<{ store: Store }, {}, {}, {}>(stores => ({
store: stores.store
}))(observer(Component as any));
}
// CLIENT-ONLY: Rehydrate JSON state to MobX
export function rehydrate(store: Store) {
// Helper to parse and rehydrate the store
const init = (data: any) => {
if (typeof data === "object") {
Object.keys(data).forEach(key => ((store as any)[key] = data[key]));
}
};
// Perform the rehydration atomically
runInAction(() => {
init((window as any).__STORE__);
try {
const parsed = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)!);
if (parsed) {
init(parsed);
}
} catch (_) {
/* Ignore localStorage parse errors */
}
});
}
// CLIENT-ONLY: Save store changes to localStorage
export function autosave(store: Store) {
autorun(() => {
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(toJS(store)));
});
}

@ -4,32 +4,32 @@
// IMPORTS
/* Node */
import * as fs from "fs";
import * as path from "path";
import fs from "fs";
import path from "path";
/* NPM */
// Koa 2 web server. Handles incoming HTTP requests, and will serve back
// the React render, or any of the static assets being compiled
import * as Koa from "koa";
import Koa from "koa";
// Static file handler
import * as koaSend from "koa-send";
import koaSend from "koa-send";
// Enable cross-origin requests
import * as koaCors from "kcors";
import koaCors from "kcors";
// Koa Router, for handling URL requests
import * as KoaRouter from "koa-router";
import KoaRouter from "koa-router";
// High-precision timing, so we can debug response time to serve a request
import * as ms from "microseconds";
import ms from "microseconds";
// Webpack 4
import * as webpack from "webpack";
import webpack from "webpack";
// Koa-specific Webpack handlers
import * as KoaWebpack from "koa-webpack";
import KoaWebpack from "koa-webpack";
/* Ora spinner */
import ora from "ora";

@ -4,14 +4,14 @@
// IMPORTS
/* NPM */
import * as chalk from "chalk";
import chalk from "chalk";
/* Local */
import { build, common } from "./app";
// ----------------------------------------------------------------------------
common.spinner.info(chalk.default.bgBlue("Build mode"));
common.spinner.info(chalk.bgBlue("Build mode"));
void (async () => {
await build();

@ -4,7 +4,7 @@
// IMPORTS
/* NPM */
import * as chalk from "chalk";
import chalk from "chalk";
/* Local */
import hotServerMiddleware from "../lib/hotServerMiddleware";
@ -13,7 +13,7 @@ import { app, common, compiler, devServer } from "./app";
// ----------------------------------------------------------------------------
common.spinner
.info(chalk.default.magenta("Development mode"))
.info(chalk.magenta("Development mode"))
.info("Building development server...");
app.listen({ port: common.port, host: common.host }, async () => {

@ -4,10 +4,10 @@
// IMPORTS
/* Node */
import * as fs from "fs";
import fs from "fs";
/* NPM */
import * as chalk from "chalk";
import chalk from "chalk";
/* Local */
import Output from "../lib/output";
@ -20,7 +20,7 @@ function getStats(file: string): IStats {
return JSON.parse(fs.readFileSync(file, "utf8")) as IStats;
}
common.spinner.info(chalk.default.green("Production mode"));
common.spinner.info(chalk.green("Production mode"));
void (async () => {
// Get a list of file accessibility

@ -4,10 +4,10 @@
// IMPORTS
/* Node */
import * as path from "path";
import path from "path";
/* NPM */
import * as chalk from "chalk";
import chalk from "chalk";
/* Local */
import { build, common, app, staticCompiler, devServer } from "./app";
@ -15,7 +15,7 @@ import clientConfig from "../webpack/client";
// ----------------------------------------------------------------------------
common.spinner.info(chalk.default.bgBlue("Static mode"));
common.spinner.info(chalk.bgBlue("Static mode"));
void (async () => {
// Production?

@ -6,7 +6,7 @@
// IMPORTS
/* NPM */
import * as React from "react";
import React from "react";
import { HelmetData } from "react-helmet";
// ----------------------------------------------------------------------------

@ -4,13 +4,13 @@
// IMPORTS
/* Node */
import * as path from "path";
import path from "path";
/* NPM */
import * as CompressionPlugin from "compression-webpack-plugin";
import CompressionPlugin from "compression-webpack-plugin";
import { mergeWith } from "lodash";
import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
import * as webpack from "webpack";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import webpack from "webpack";
const BrotliCompression = require("brotli-webpack-plugin");
/* Local */

@ -4,11 +4,11 @@
// IMPORTS
/* Node */
import * as path from "path";
import path from "path";
/* NPM */
import * as lodash from "lodash";
import * as webpack from "webpack";
import lodash from "lodash";
import webpack from "webpack";
// ----------------------------------------------------------------------------
@ -69,25 +69,15 @@ export default (_ssr: boolean /* <-- not currently used */) => {
loader: "babel-loader",
options: {
cacheDirectory: true,
presets: ["@babel/typescript", "@babel/react"],
plugins: [
["@babel/proposal-class-properties", { loose: true }],
"@babel/proposal-object-rest-spread",
"@babel/plugin-syntax-dynamic-import",
"react-hot-loader/babel",
"emotion"
]
}
},
{
loader: "ts-loader",
options: {
compilerOptions: {
module: "esnext"
},
// Avoid typechecking, to speed up bundling. To avoid the
// complexity of type checking *both* @launch/app and a project's
// `tsconfig.json`, this should be a userland exercise
transpileOnly: true
}
}
]
}

@ -4,7 +4,7 @@
// IMPORTS
/* NPM */
import * as MiniCssExtractPlugin from "mini-css-extract-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import { Loader } from "webpack";
// ----------------------------------------------------------------------------

@ -4,11 +4,11 @@
// IMPORTS
/* Node */
import * as path from "path";
import path from "path";
/* NPM */
import { mergeWith } from "lodash";
import * as webpack from "webpack";
import webpack from "webpack";
/* Local */
import common, { defaultMerger, files } from "./common";

@ -6,11 +6,11 @@
/* NPM */
import { mergeWith } from "lodash";
import * as webpack from "webpack";
import webpack from "webpack";
import {} from "webpack-dev-server";
// Plugin for generating `index.html` file for static hosting
import * as HtmlWebpackPlugin from "html-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
/* Local */

@ -1,26 +1,30 @@
{
"compilerOptions": {
// Incremental mode
"incremental": true,
"allowJs": false,
"allowUnusedLabels": true,
"baseUrl": ".",
"experimentalDecorators": true,
"declaration": true,
"jsx": "react",
"lib": ["dom", "es2015", "esnext"],
// Target latest version of ECMAScript.
"target": "esnext",
// Search under node_modules for non-relative imports.
"moduleResolution": "node",
// Set module format
"module": "commonjs",
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist/ts",
// Process & infer types from .js files.
"allowJs": true,
// Don't emit; allow Babel to transform files.
"noEmit": true,
// Enable strictest settings like strictNullChecks & noImplicitAny.
"strict": true,
// Import non-ES modules as default imports.
"esModuleInterop": true,
// Enable React
"jsx": "preserve",
// Set the base path
"baseUrl": ".",
// Source paths
"paths": {
"@/*": ["src/*"],
"brotli-webpack-plugin": ["types/modules"],
"microseconds": ["types/microseconds"]
},
"removeComments": true,
"sourceMap": true,
"strict": true,
"target": "es2017"
"microseconds": ["types/microseconds.d.ts"]
}
},
"exclude": ["dist", "__tests__", "wallaby.js"]
"include": ["src", "types"]
}

1
types/fonts.d.ts vendored

@ -1,3 +1,4 @@
// Fonts
declare module "*.eot" {
const value: string;
export default value;

@ -1,3 +1,4 @@
// Globals
declare var GRAPHQL: string;
declare var SERVER: boolean;