├── .editorconfig
├── shared
├── css
│ └── var
│ │ ├── var.css.d.ts
│ │ └── var.css
├── images
│ ├── boil.jpg
│ ├── boil2.jpg
│ ├── cancel.svg
│ ├── boy.svg
│ ├── girl.svg
│ └── boil.svg
├── icon
│ └── favicon.png
├── signal
│ ├── OneSignalSDKWorker.js
│ └── OneSignalSDKUpdaterWorker.js
├── components
│ ├── toastrCSS.tsx
│ ├── scripter.tsx
│ ├── styler.tsx
│ └── MUIProvider
│ │ └── index.tsx
├── declarations.d.tsx
├── helmet
│ └── index.tsx
└── theme
│ └── index.tsx
├── internal
├── test
│ ├── jest.js
│ └── jestSetup.js
├── docker
│ ├── Dockerfile.dev
│ └── Dockerfile
├── kubernetes
│ ├── service.yaml
│ ├── ingress.yaml
│ └── frontend.yaml
└── webpack
│ ├── extras.js
│ ├── css.js
│ ├── loaders.js
│ └── plugins.js
├── .eslintrc.js
├── app
├── components
│ ├── 404
│ │ ├── 404.tsx
│ │ ├── __snapshots__
│ │ │ └── 404.test.tsx.snap
│ │ └── 404.test.tsx
│ ├── home
│ │ ├── css
│ │ │ ├── home.css.d.ts
│ │ │ └── home.css
│ │ └── index.tsx
│ ├── shell
│ │ ├── shell.css.d.ts
│ │ ├── shell.css
│ │ └── index.tsx
│ └── user
│ │ ├── css
│ │ ├── teste.css.d.ts
│ │ └── teste.css
│ │ ├── country
│ │ ├── country.test.tsx
│ │ ├── __snapshots__
│ │ │ └── country.test.tsx.snap
│ │ └── country.tsx
│ │ ├── item
│ │ ├── item.tsx
│ │ ├── item.test.tsx
│ │ └── __snapshots__
│ │ │ └── item.test.tsx.snap
│ │ ├── user.test.tsx
│ │ ├── index.tsx
│ │ ├── user.tsx
│ │ └── __snapshots__
│ │ └── user.test.tsx.snap
├── router.tsx
└── route.map.tsx
├── .gitignore
├── webpack.config.js
├── .travis.yml
├── store
├── logger.tsx
├── epics.tsx
├── actions.tsx
├── createStore.tsx
└── reducers.tsx
├── tsconfig.json
├── webpack.dll.js
├── tslint.json
├── server
├── flush.tsx
├── renderer.tsx
├── middleware.tsx
├── html.tsx
├── server.js
├── helpers.tsx
└── index.html
├── webpack.server.js
├── client
├── client.tsx
├── offline.tsx
├── sw.tsx
└── renderer.tsx
├── webpack.client.js
├── hot.js
├── dobi.yaml
├── README.org
└── package.json
/.editorconfig:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/shared/css/var/var.css.d.ts:
--------------------------------------------------------------------------------
1 | export default {};
2 |
--------------------------------------------------------------------------------
/internal/test/jest.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub';
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "cesco"
3 | };
4 |
--------------------------------------------------------------------------------
/app/components/home/css/home.css.d.ts:
--------------------------------------------------------------------------------
1 | export const container: string
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | dll/
3 | .awcache/
4 |
5 | node_modules/
6 | .wercker/
7 | coverage/
8 |
--------------------------------------------------------------------------------
/shared/images/boil.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cescoferraro/react-boil/HEAD/shared/images/boil.jpg
--------------------------------------------------------------------------------
/shared/icon/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cescoferraro/react-boil/HEAD/shared/icon/favicon.png
--------------------------------------------------------------------------------
/shared/images/boil2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cescoferraro/react-boil/HEAD/shared/images/boil2.jpg
--------------------------------------------------------------------------------
/shared/css/var/var.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --mainColor: red;
3 | --contentHeight: calc( 100vh -64px );
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = env => [
2 | require('./webpack.client.js')(env),
3 | require('./webpack.server.js')(env)
4 | ];
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 | before_script:
5 | - npm run prod
6 | script:
7 | - npm run lint
8 | - npm run test
9 |
--------------------------------------------------------------------------------
/internal/docker/Dockerfile.dev:
--------------------------------------------------------------------------------
1 | FROM node:6
2 | RUN npm i -g typescript
3 | RUN npm i -g tslint
4 | RUN npm i -g webpack
5 |
6 | # GOPATH
7 | WORKDIR /srv/boil
8 |
--------------------------------------------------------------------------------
/shared/signal/OneSignalSDKWorker.js:
--------------------------------------------------------------------------------
1 | importScripts('https://boil.cescoferraro.xyz/service-worker.js');
2 | importScripts('https://cdn.onesignal.com/sdks/OneSignalSDK.js');
3 |
--------------------------------------------------------------------------------
/shared/signal/OneSignalSDKUpdaterWorker.js:
--------------------------------------------------------------------------------
1 | importScripts('https://boil.cescoferraro.xyz/service-worker.js');
2 | importScripts('https://cdn.onesignal.com/sdks/OneSignalSDK.js');
3 |
--------------------------------------------------------------------------------
/app/components/shell/shell.css.d.ts:
--------------------------------------------------------------------------------
1 | export const base: string
2 | export const container: string
3 | export const button: string
4 | export const flex: string
5 | export const main: string
6 | export const svg: string
7 |
--------------------------------------------------------------------------------
/internal/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mhart/alpine-node:latest
2 | WORKDIR /srv/acharh
3 | RUN npm i express@5.0.0-alpha.5
4 | RUN npm i compression
5 | RUN npm i morgan
6 | ADD dist/ /srv/acharh/
7 | CMD ["node","server.js"]
8 |
--------------------------------------------------------------------------------
/app/components/404/404.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | export const NoMatchContainer = (props) => {
4 | return (
5 |
6 |
NOT FOUND!
7 |
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/app/components/home/css/home.css:
--------------------------------------------------------------------------------
1 | .container {
2 | overflow:auto;
3 | margin-left:10px;
4 | margin-right:10px;
5 | }
6 |
7 | table {
8 | border-collapse: separate;
9 | border-spacing: 20px 20px;
10 | }
11 |
--------------------------------------------------------------------------------
/internal/test/jestSetup.js:
--------------------------------------------------------------------------------
1 | const enzyme = require('enzyme');
2 | global.shallow = enzyme.shallow;
3 | global.render = enzyme.render;
4 | global.mount = enzyme.mount;
5 | // Fail tests on any warning
6 | console.error = message => {
7 | throw new Error(message);
8 | };
9 |
--------------------------------------------------------------------------------
/shared/components/toastrCSS.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Styler } from "./styler"
3 | const rules = require("-!raw-loader!react-redux-toastr/lib/css/react-redux-toastr.min.css")
4 | export const ToastrCSS = () =>
5 |
--------------------------------------------------------------------------------
/internal/kubernetes/service.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | namespace: boil
6 | name: boil-svc
7 | spec:
8 | selector:
9 | name: boil-pods
10 | ports:
11 | - name: web
12 | port: 5000
13 | targetPort: 5000
14 | protocol: TCP
15 |
--------------------------------------------------------------------------------
/app/components/home/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as CSS from "./css/home.css"
3 | const test = require("../../../README.org")
4 |
5 | export const HomeComponent = ({ }) => {
6 | return
7 | }
8 |
--------------------------------------------------------------------------------
/shared/components/scripter.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | export const Scripter = ({ rules, async, id }) => {
3 | return (
4 | )
9 | }
10 |
--------------------------------------------------------------------------------
/shared/components/styler.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | export const Styler = ({ rules, id }) => {
3 | return (
4 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/shared/declarations.d.tsx:
--------------------------------------------------------------------------------
1 | declare const require: any
2 | declare const module: any
3 | declare let global: any
4 | declare let test: any
5 | declare let expect: any
6 | declare let shallow: any
7 | declare let render: any
8 | declare let mount: any
9 | declare var describe: any
10 | declare var it: any
11 |
--------------------------------------------------------------------------------
/app/components/404/__snapshots__/404.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[` 404 1`] = `
4 |
5 |
6 |
7 |
8 |
9 | NOT FOUND!
10 |
11 |
12 |
13 |
14 |
15 | `;
16 |
--------------------------------------------------------------------------------
/app/components/user/css/teste.css.d.ts:
--------------------------------------------------------------------------------
1 | export const test: string
2 | export const flag: string
3 | export const flex: string
4 | export const title: string
5 | export const image: string
6 | export const name: string
7 | export const item: string
8 | export const itemIcon: string
9 | export const itemText: string
10 | export const value: string
11 | export const key: string
12 | export const button: string
13 |
--------------------------------------------------------------------------------
/internal/kubernetes/ingress.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: extensions/v1beta1
3 | kind: Ingress
4 | metadata:
5 | namespace: boil
6 | name: boil-ing
7 | spec:
8 | tls:
9 | - hosts:
10 | - boil.cescoferraro.xyz
11 | secretName: wild-tls
12 | rules:
13 | - host: boil.cescoferraro.xyz
14 | http:
15 | paths:
16 | - path: /
17 | backend:
18 | serviceName: boil-svc
19 | servicePort: 5000
20 |
--------------------------------------------------------------------------------
/shared/components/MUIProvider/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider"
3 |
4 | /* import getMuiTheme from "material-ui/styles/getMuiTheme"*/
5 | /* import { BoilTheme } from "../../theme"*/
6 | /* */
7 |
8 | export const MUIProvider = ({ children }) => (
9 | < MuiThemeProvider >
10 | {children}
11 |
12 | )
13 |
--------------------------------------------------------------------------------
/store/logger.tsx:
--------------------------------------------------------------------------------
1 | import { createLogger } from "redux-logger"
2 |
3 | /* const beginsWith = (needle, haystack) => {*/
4 | /* return (haystack.substr(0, needle.length) === needle)*/
5 | /* }*/
6 |
7 | export const isServer = () => !(typeof window !== "undefined" && window.document)
8 |
9 | export const logger = createLogger({
10 | collapsed: (getState, action, logEntry) => !logEntry.error,
11 | predicate: (getState, action) => {
12 | return !isServer()
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "lib": [
5 | "es2016",
6 | "dom"
7 | ],
8 | "target": "es5",
9 | "jsx": "react",
10 | "sourceMap": true,
11 | "emitDecoratorMetadata": true,
12 | "experimentalDecorators": true,
13 | "outDir": "dist",
14 | "typeRoots": [
15 | "node_modules@types"
16 | ]
17 | },
18 | "rules": {
19 | "curly": true
20 | },
21 | "rulesDirectory": [
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/app/components/user/country/country.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import Country from "./country"
3 | import { MUIProvider } from "../../../../shared/components/MUIProvider"
4 |
5 | test("Render an Item", () => {
6 | const component = render(
7 |
8 |
12 |
13 | )
14 | expect(component).toMatchSnapshot()
15 | })
16 |
--------------------------------------------------------------------------------
/webpack.dll.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 |
4 | module.exports = env => ({
5 | entry: {
6 | vendor: ['react', 'react-dom']
7 | },
8 | output: {
9 | filename: '[name].dll.js',
10 | library: '[name]',
11 | path: path.join(__dirname, 'dll')
12 | },
13 | plugins: [
14 | new webpack.DllPlugin({
15 | context: __dirname,
16 | name: '[name]',
17 | path: path.join(__dirname, 'dll', '[name].dll.json')
18 | })
19 | ]
20 | });
21 |
--------------------------------------------------------------------------------
/app/components/user/item/item.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as CSS from "../css/teste.css"
3 |
4 | export const Item = ({ icon, title, value }) => {
5 | return (
6 |
7 |
8 | {icon}
9 |
10 |
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {"extends": ["tslint:latest", "tslint-react"],
2 | "rules":{
3 | "semicolon": [true, "never"],
4 | "trailing-comma": [
5 | true,
6 | {
7 | "multiline": {
8 | "objects": "never",
9 | "arrays": "never",
10 | "functions": "never",
11 | "typeLiterals": "ignore"
12 | }
13 | }
14 | ],
15 | "align": [true, "parameters", "statements"],
16 | "no-console": [false],
17 | "no-var-requires": false,
18 | "no-unused-variable": true,
19 | "ordered-imports": false,
20 | "object-literal-sort-keys":false
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internal/kubernetes/frontend.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: extensions/v1beta1
3 | kind: Deployment
4 | metadata:
5 | namespace: boil
6 | name: boil-deploy
7 | spec:
8 | replicas: 1
9 | template:
10 | metadata:
11 | namespace: boil
12 | labels:
13 | name: boil-pods
14 | spec:
15 | containers:
16 | - image: cescoferraro/boil:latest
17 | imagePullPolicy: Always
18 | name: frontend
19 | env:
20 | - name: KUBERNETES
21 | value: "true"
22 | - name: NODE_ENV
23 | value: "production"
24 |
--------------------------------------------------------------------------------
/app/components/404/404.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { NoMatchContainer } from "./404"
3 | import { profileStartup } from "../../../store/reducers"
4 | import { MUIProvider } from "../../../shared/components/MUIProvider"
5 |
6 | describe("", () => {
7 | it('404', () => {
8 | const component = mount(
9 |
10 |
11 |
12 | )
13 | expect(component.text()).toContain('NOT FOUND!');
14 | expect(component).toMatchSnapshot()
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/app/components/user/country/__snapshots__/country.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render an Item 1`] = `
4 |
7 |
10 |

15 |
16 |
30 |
31 | `;
32 |
--------------------------------------------------------------------------------
/app/components/shell/shell.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Roboto');
2 |
3 | html {
4 | font-family: 'Roboto', sans-serif;
5 | font-size: 20px;
6 | }
7 |
8 | .base{
9 | position: fixed !important;
10 | }
11 |
12 | .container{
13 | height: calc( 100vh - 64px );
14 | overflow: auto;
15 | }
16 |
17 | .button {
18 | height: 48px;
19 | width: 48px;
20 | }
21 | .flex{
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | }
26 |
27 | .main{
28 | height: 30vh;
29 | }
30 | .svg{
31 | height:20vh;
32 | width: 40vw;
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/components/user/user.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { UserComponent } from "./user"
3 | import { profileStartup } from "../../../store/reducers"
4 | import { MUIProvider } from "../../../shared/components/MUIProvider"
5 |
6 | test("Render a User", () => {
7 | const component = render(
8 |
9 |
14 |
15 | )
16 | expect(component).toMatchSnapshot()
17 | })
18 |
--------------------------------------------------------------------------------
/app/components/user/item/item.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Item } from "./item"
3 | import GpsFixed from "material-ui/svg-icons/device/gps-fixed"
4 | import { MUIProvider } from "../../../../shared/components/MUIProvider"
5 |
6 | test("Render an Item", () => {
7 | const icon = ()
8 | const component = mount(
9 |
10 |
15 |
16 | )
17 | expect(component).toMatchSnapshot()
18 | })
19 |
--------------------------------------------------------------------------------
/store/epics.tsx:
--------------------------------------------------------------------------------
1 | import "rxjs/add/operator/map"
2 | import "rxjs/add/observable/of"
3 | import "rxjs/add/operator/mapTo"
4 | import "rxjs/add/operator/mergeMap"
5 | import "rxjs/add/operator/filter"
6 | import { combineEpics } from "redux-observable"
7 |
8 | export const PING_ACTION_NAME = "PING"
9 | export function PING_ACTION() {
10 | return {
11 | type: PING_ACTION_NAME
12 | }
13 | }
14 |
15 | const pingEpic = (action$) =>
16 | action$.filter((action) => action.type === "PING")
17 | .map((hey) => (hey))
18 | .mapTo({ type: "USER", payload: 78878 })
19 |
20 | export const RootEpic = combineEpics(
21 | pingEpic
22 | )
23 |
--------------------------------------------------------------------------------
/app/components/user/index.tsx:
--------------------------------------------------------------------------------
1 | import universal from "react-universal-component"
2 | declare var System: any
3 | import * as importCss from 'babel-plugin-universal-import/importCss.js'
4 | import * as universalImport from 'babel-plugin-universal-import/universalImport.js'
5 |
6 |
7 | declare var __dirname: any
8 | export const AsyncUser = universal(
9 | universalImport({
10 | load: () => Promise.all([
11 | System.import(/* webpackChunkName: 'user' */ "./user"),
12 | importCss("user")]).then(promises => promises[0]),
13 | chunkName: () => "user",
14 | resolve: () => require.resolveWeak('./user'),
15 | })
16 | )
17 |
18 |
--------------------------------------------------------------------------------
/server/flush.tsx:
--------------------------------------------------------------------------------
1 | import { flushModuleIds } from "react-universal-component/server"
2 | import flushChunks from "webpack-flush-chunks"
3 | import { flushChunkNames } from "react-universal-component/server"
4 |
5 | export const flushedAssets = (clientStats, outputPath, production) => {
6 | const moduleIds = flushModuleIds()
7 | const chunkNames = flushChunkNames()
8 | return {
9 | moduleIds,
10 | chunkNames,
11 | ...flushChunks(clientStats, {
12 | chunkNames,
13 | before: production ? ["bootstrap", "vendor"] : ["bootstrap"],
14 | after: ["main"],
15 | outputPath
16 | })
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/webpack.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const dist = path.join(__dirname, 'dist');
3 | const extras = require('./internal/webpack/extras.js');
4 | const plugins = require('./internal/webpack/plugins.js');
5 | const loaders = require('./internal/webpack/loaders.js');
6 |
7 | module.exports = env => ({
8 | devtool: extras.devTools(env),
9 | entry: './server/middleware',
10 | externals: { vertx: 'vertx' },
11 | module: loaders.serverLoaders(env),
12 | name: 'server',
13 | output: {
14 | filename: 'server/[name].js',
15 | libraryTarget: 'commonjs2',
16 | path: dist,
17 | publicPath: extras.publicPath(env)
18 | },
19 | plugins: plugins.serverPlugins(env),
20 | resolve: extras.resolve,
21 | target: 'node'
22 | });
23 |
--------------------------------------------------------------------------------
/internal/webpack/extras.js:
--------------------------------------------------------------------------------
1 | const resolve = {
2 | extensions: ['.css', '.js', '.tsx', '.json', '.pcss']
3 | };
4 |
5 | const devTools = env =>
6 | env.production ? 'source-map' : 'cheap-module-eval-source-map';
7 |
8 | const publicPath = env =>
9 | env.production ? 'https://boil.cescoferraro.xyz/' : 'http://localhost:5000/';
10 |
11 | const hotLoader = (entry, env) => {
12 | if (!env.production) {
13 | return [
14 | 'webpack-hot-middleware/client?' +
15 | 'path=/__webpack_hmr&timeout=20000&reload=true',
16 | 'react-hot-loader/patch',
17 | entry
18 | ];
19 | }
20 | return entry;
21 | };
22 |
23 | module.exports = {
24 | resolve: resolve,
25 | devTools: devTools,
26 | publicPath: publicPath,
27 | hotLoader: hotLoader
28 | };
29 |
--------------------------------------------------------------------------------
/server/renderer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { renderToString } from "react-dom/server"
3 | import { AppRouter } from "../app/router"
4 | import getMuiTheme from "material-ui/styles/getMuiTheme"
5 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider"
6 | import { Provider as ReduxProvider } from "react-redux"
7 | import { BoilTheme } from "../shared/theme"
8 |
9 | export const Renderer = (req, store) => {
10 | const app = (
11 |
12 |
13 |
14 |
15 |
16 | )
17 | return { app, string: renderToString(app) }
18 | }
19 |
--------------------------------------------------------------------------------
/app/components/user/country/country.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Item } from "../item/item"
3 | import * as Flag from "react-flags"
4 |
5 | class Country extends React.Component {
6 | public render() {
7 | const icon = (
8 |
16 | )
17 | return (
18 |
23 | )
24 | }
25 | }
26 |
27 | export default Country
28 |
--------------------------------------------------------------------------------
/client/client.tsx:
--------------------------------------------------------------------------------
1 | import { AppRouter } from "../app/router"
2 | import { Renderer, tag } from "./renderer"
3 | import * as injectTapEventPlugin from "react-tap-event-plugin"
4 | import { unmountComponentAtNode } from "react-dom"
5 | import { runServiceWorker } from "./sw"
6 | injectTapEventPlugin()
7 | require("offline-js")
8 |
9 | if (module.hot) {
10 | module.hot.accept(
11 | [
12 | "../store/createStore.tsx",
13 | "../app/router.tsx",
14 | "./renderer.tsx"
15 | ],
16 | () => {
17 | unmountComponentAtNode(tag)
18 | const NextEatApp = require("../app/router.tsx").AppRouter
19 | const NewRenderer = require("./renderer.tsx").Renderer
20 | NewRenderer(NextEatApp)
21 | })
22 | }
23 |
24 | Renderer(AppRouter)
25 | runServiceWorker()
26 |
--------------------------------------------------------------------------------
/store/actions.tsx:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from "redux"
2 | export const DRAWER_ACTION_NAME = "DRAWER"
3 |
4 | export function DRAWER_ACTION(state) {
5 | return {
6 | type: DRAWER_ACTION_NAME,
7 | payload: state
8 | }
9 | }
10 |
11 | export const DRAWER_TOGGLE_ACTION_NAME = "DRAWER_TOOGLE"
12 | export function DRAWER_TOGGLE_ACTION() {
13 | return {
14 | type: DRAWER_TOGGLE_ACTION_NAME
15 | }
16 | }
17 |
18 | export function USER_ACTION(id) {
19 | return { type: "USER", payload: { id } }
20 | }
21 |
22 | export function GO_HOME_ACTION(id) {
23 | return { type: "HOME" }
24 | }
25 |
26 | export const APP_ACTIONS = (dispatch) => {
27 | return bindActionCreators({
28 | DRAWER_ACTION,
29 | USER_ACTION,
30 | GO_HOME_ACTION,
31 | DRAWER_TOGGLE_ACTION,
32 | dispatch
33 | }, dispatch)
34 | }
35 |
--------------------------------------------------------------------------------
/client/offline.tsx:
--------------------------------------------------------------------------------
1 | import { toastr } from "react-redux-toastr"
2 |
3 | const upHandler = (e) => {
4 | toastr.success("The UP", "cloud 9", {
5 | position: "bottom-left"
6 | })
7 | console.log("upppp")
8 | }
9 | const downHandler = (e) => {
10 | toastr.error("The down", "rot in hell", {
11 | position: "bottom-left"
12 | })
13 | console.log("downnnnn")
14 | }
15 |
16 | export const offlineCheck = (store) => {
17 | const { Offline } = (window as any)
18 | Offline.options = {
19 | checkOnLoad: true,
20 | checks: {
21 | image: {
22 | url: () => ("https://esri.github.io/offline-editor-js/tiny-image.png?_="
23 | + (Math.floor(Math.random() * 1000000000)))
24 |
25 | },
26 | active: "image"
27 | }
28 | }
29 | Offline.on("up", upHandler)
30 | Offline.on("down", downHandler)
31 | }
32 |
--------------------------------------------------------------------------------
/server/middleware.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { renderToStaticMarkup } from "react-dom/server"
3 | import { HTML } from "./html"
4 | import * as injectTapEventPlugin from "react-tap-event-plugin"
5 | import { configureStore } from "../store/createStore"
6 | import { Renderer } from "./renderer"
7 | import createHistory from "history/createMemoryHistory"
8 | global.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest
9 | injectTapEventPlugin()
10 |
11 | export default function serverRenderer(props) {
12 | return (req, res, next) => {
13 | const store = configureStore(createHistory({ initialEntries: [req.path] }))
14 | const render = Renderer(req, store)
15 | res.send("" +
16 | renderToStaticMarkup(
17 |
22 | ))
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/webpack.client.js:
--------------------------------------------------------------------------------
1 | const extras = require('./internal/webpack/extras.js');
2 | const path = require('path');
3 | const plugins = require('./internal/webpack/plugins.js');
4 | const loaders = require('./internal/webpack/loaders.js');
5 |
6 | module.exports = env => {
7 | const client = {
8 | devtool: extras.devTools(env),
9 | entry: {
10 | main: extras.hotLoader(
11 | path.resolve(__dirname, './client/client.tsx'),
12 | env
13 | )
14 | },
15 | externals: {
16 | vertx: 'vertx'
17 | },
18 | module: loaders.clientLoaders(env),
19 | name: 'client',
20 | output: {
21 | filename: 'js/[name]_[hash].js',
22 | path: path.join(__dirname, 'dist'),
23 | publicPath: extras.publicPath(env)
24 | },
25 | plugins: plugins.clientPlugins(env),
26 | resolve: extras.resolve,
27 | target: 'web'
28 | };
29 | if (env.production) {
30 | client.entry.vendorC = [
31 | 'react',
32 | 'lodash',
33 | 'react-dom',
34 | 'rxjs',
35 | 'material-ui'
36 | ];
37 | }
38 | return client;
39 | };
40 |
--------------------------------------------------------------------------------
/shared/helmet/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Helmet } from "react-helmet"
3 | const Cesco = require("../images/boil.jpg")
4 |
5 | export const MyHelmet = ({ title }) => (
6 |
7 |
8 |
9 |
10 | {title + " | React-boil"}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 |
23 | export const UserHelmet = ({ id }) => (
24 |
25 |
26 |
27 | )
28 |
--------------------------------------------------------------------------------
/hot.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const express = require('express');
3 | const webpack = require('webpack');
4 | const webpackDevMiddleware = require('webpack-dev-middleware');
5 | const webpackHotMiddleware = require('webpack-hot-middleware');
6 | const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
7 | const config = require('./webpack.config.js')({ production: false });
8 | const clientConfig = require('./webpack.client.js')({ production: false });
9 | const app = express();
10 | const compiler = webpack(config);
11 | const publicPath = clientConfig.output.publicPath;
12 | const outputPath = clientConfig.output.path;
13 | app.use('/dll', express.static(path.join(__dirname, 'dll')));
14 | app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath }));
15 | app.use(
16 | webpackHotMiddleware(compiler.compilers.find(comp => comp.name === 'client'))
17 | );
18 | app.use(
19 | webpackHotServerMiddleware(compiler, {
20 | serverRendererOptions: {
21 | outputPath,
22 | production: false
23 | }
24 | })
25 | );
26 |
27 | app.listen(5000, () => {
28 | console.log('Server started: http://localhost:5000');
29 | });
30 |
--------------------------------------------------------------------------------
/client/sw.tsx:
--------------------------------------------------------------------------------
1 | export const runServiceWorker = () => {
2 | if (document.location.hostname !== "localhost") {
3 | if ("serviceWorker" in navigator) {
4 | navigator.serviceWorker.register("/OneSignalSDKWorker.js").then((reg) => {
5 | reg.onupdatefound = () => {
6 | const installingWorker = reg.installing
7 | installingWorker.onstatechange = () => {
8 | switch (installingWorker.state) {
9 | case "installed":
10 | if (navigator.serviceWorker.controller) {
11 | console.log("New or updated content is available.")
12 | } else {
13 | console.log("Content is now available offline!")
14 | }
15 | break
16 | case "redundant":
17 | console.error("The installing service worker became redundant.")
18 | break
19 | }
20 | }
21 | }
22 | }).catch((e) => {
23 | console.error("Error during service worker registration:", e)
24 | })
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/shared/images/cancel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
44 |
--------------------------------------------------------------------------------
/shared/theme/index.tsx:
--------------------------------------------------------------------------------
1 | const spacing = {
2 | iconSize: 24,
3 | desktopGutter: 24,
4 | desktopGutterMore: 32,
5 | desktopGutterLess: 16,
6 | desktopGutterMini: 8,
7 | desktopKeylineIncrement: 64,
8 | desktopDropDownMenuItemHeight: 32,
9 | desktopDropDownMenuFontSize: 15,
10 | desktopDrawerMenuItemHeight: 48,
11 | desktopSubheaderHeight: 48,
12 | desktopToolbarHeight: 56
13 | }
14 | /**
15 | * Light Theme is the default theme used in material-ui. It is guaranteed to
16 | * have all theme variables needed for every component. Variables not defined
17 | * in a custom theme will default to these values.
18 | */
19 | export const BoilTheme = {
20 | spacing,
21 | fontFamily: "Roboto, sans-serif",
22 | borderRadius: 2,
23 | palette: {
24 | primary1Color: "#303F9F",
25 | primary2Color: "#3F51B5",
26 | primary3Color: "#C5CAE9",
27 | accent1Color: "#C5CAE9",
28 | accent2Color: "#C5CAE9",
29 | accent3Color: "#C5CAE9",
30 | textColor: "#212121",
31 | secondaryTextColor: " #757575",
32 | /* alternateTextColor: darkBlack,*/
33 | /* canvasColor: white,*/
34 | borderColor: "#BDBDBD",
35 | disabledColor: "#BDBDBD"
36 | /* pickerHeaderColor: cyan500,*/
37 | /* clockCircleColor: fade(darkBlack, 0.07),*/
38 | /* shadowColor: fullBlack,*/
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/router.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { connect } from "react-redux"
3 | import { compose } from "recompose"
4 | import { APP_ACTIONS } from "../store/actions"
5 | import { NoMatchContainer } from "./components/404/404"
6 | import { Shell } from "./components/shell/index"
7 | import { MyHelmet } from "../shared/helmet/index"
8 | import { AsyncUser } from "./components/user"
9 | import { HomeComponent } from "./components/home"
10 |
11 | const AppRouterClass = (props) => {
12 | switch (props.location.type) {
13 | case "HOME":
14 | return (
15 |
16 |
17 |
18 |
19 | )
20 | case "USER":
21 | return (
22 |
23 |
24 |
25 |
26 | )
27 | default:
28 | return (
29 |
30 |
31 |
32 |
33 | )
34 | }
35 | }
36 |
37 | export const AppRouter = compose(
38 | connect(({ location, userId, profile, drawer, profiles }) =>
39 | ({ location, userId, profile, drawer, profiles }), APP_ACTIONS)
40 | )(AppRouterClass)
41 |
--------------------------------------------------------------------------------
/client/renderer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as ReactDOM from "react-dom"
3 | import { AppContainer } from "react-hot-loader"
4 | import getMuiTheme from "material-ui/styles/getMuiTheme"
5 | import MuiThemeProvider from "material-ui/styles/MuiThemeProvider"
6 | import { Provider as ReduxProvider } from "react-redux"
7 | import { configureStore, engine } from "../store/createStore"
8 | import { createBrowserHistory } from "history"
9 | import { BoilTheme } from "../shared/theme"
10 | import * as storage from "redux-storage"
11 | import { toastr } from "react-redux-toastr"
12 | import { offlineCheck } from "./offline"
13 |
14 | export const tag = document.getElementById("root")
15 |
16 |
17 |
18 | export const Renderer = (Component) => {
19 | const history = createBrowserHistory()
20 | const store = configureStore(history)
21 | offlineCheck(store)
22 | if ((window as any).__PRODUCTION__) {
23 | storage.createLoader(engine)(store)
24 | .then((newState) => { store.dispatch(newState.location) })
25 | .catch(() => console.log("Failed to load previous state"))
26 | }
27 | const boilMUI = getMuiTheme(BoilTheme, { userAgent: navigator.userAgent })
28 | ReactDOM.render(
29 |
30 |
31 |
32 |
33 |
34 |
35 | , tag)
36 | }
37 |
--------------------------------------------------------------------------------
/app/route.map.tsx:
--------------------------------------------------------------------------------
1 | import { profileStartup } from "../store/reducers"
2 | import { Observable } from "rxjs/Observable"
3 | import "rxjs/add/operator/map"
4 | import "rxjs/add/operator/delay"
5 | import "rxjs/add/observable/dom/ajax"
6 | import "rxjs/add/observable/of"
7 | import "rxjs/add/observable/if"
8 | import { isServer } from "../store/logger"
9 |
10 | const userThunk = (dispatch, getState) => {
11 | const { id } = getState().location.payload
12 | console.log(isServer())
13 | const { Offline } = (window as any)
14 | Offline.check()
15 | Observable.of(Offline.state)
16 | .delay(new Date(Date.now() + 100))
17 | .flatMap((state) => Observable.of(Offline.state))
18 | .flatMap((state) => (
19 | Observable.if(
20 | () => (state === "up" ? true : false),
21 | Observable.ajax("https://randomuser.me/api")
22 | .map((user) => (user.response.results[0]))
23 | .map((user) => {
24 | user.id = id
25 | console.log(getState())
26 | dispatch({ type: "USER_FOUND", payload: { user } })
27 | }),
28 | Observable.of(profileStartup)
29 | .map((user) => {
30 | user.id = id
31 | dispatch({ type: "USER_FOUND", payload: { user } })
32 | })
33 | )))
34 | .subscribe((success) => {
35 | console.log("done")
36 | })
37 |
38 | }
39 |
40 | export const routesMap = {
41 | HOME: { path: "/" },
42 | USER: { path: "/user/:id", thunk: userThunk }
43 | }
44 |
--------------------------------------------------------------------------------
/app/components/user/css/teste.css:
--------------------------------------------------------------------------------
1 | @import "../../../../shared/css/var/var.css";
2 | .button{
3 | width:40px;
4 | height:40px;
5 |
6 | }
7 | .test{
8 | background-color: orange;
9 | height: 100%;
10 | overflow:auto;
11 | }
12 | .flag {
13 | object-fit: cover;
14 | width:32px;
15 | height:32px;
16 | border-radius: 50%;
17 | }
18 |
19 | .flex{
20 | display:flex;
21 | align-items: center;
22 | }
23 |
24 | .title {
25 | height: calc(var(--contentHeight)* 0.35);
26 | padding:30px;
27 | }
28 |
29 | .image{
30 | height: calc(var(--contentHeight)* 0.25);
31 | width: calc(var(--contentHeight)* 0.25);
32 | display: block;
33 | margin:auto;
34 | }
35 | .name{
36 | display: block;
37 | margin:auto;
38 | height: calc(var(--contentHeight)* 0.10);
39 | display:flex;
40 | justify-content:center;
41 | align-items: center;
42 | }
43 | .item {
44 | height: calc(var(--contentHeight)* 0.10);
45 | display:flex;
46 | }
47 | .itemText{
48 | height: calc(var(--contentHeight)* 0.10) !important;
49 | width: 85vw;
50 | display: inline-grid;
51 | }
52 | .itemIcon {
53 | display:flex;
54 | justify-content:center;
55 | align-items: center;
56 | width: 15vw;
57 | }
58 | .key {
59 | height: calc(var(--contentHeight)* 0.10 * 0.3) !important;
60 | font-size: calc(var(--contentHeight)* 0.10 * 0.22) !important;
61 | width: 85vw;
62 | vertical-align: middle;
63 | margin: 0px;
64 | color: grey;
65 | }
66 |
67 | .value {
68 | height: calc(var(--contentHeight)* 0.10 * 0.7) !important;
69 | width: 85vw;
70 | font-size: calc(var(--contentHeight)* 0.10 * 0.3) !important;
71 | vertical-align: middle;
72 | margin: 0px;
73 | }
--------------------------------------------------------------------------------
/server/html.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import {
3 | getScripts, getStyles, Helmator, Manifest,
4 | OneSignalCDN, OneSignalInit, App, Dll, Javascript, BaseStyle
5 | } from "./helpers"
6 | import { flushedAssets } from "./flush"
7 | import { ToastrCSS } from "../shared/components/toastrCSS"
8 | import * as serialize from "serialize-javascript"
9 |
10 | export const HTML = (
11 | { clientStats, serverStats, outputPath, production, content, store }
12 | ) => {
13 | const assets = flushedAssets(clientStats, outputPath, production)
14 | const { preload, scripts } = getScripts(assets.scripts, outputPath, production)
15 | const styles = getStyles(assets.stylesheets, outputPath, production)
16 | const MyHelmet = Helmator()
17 | const inner = {
18 | __html: `window.__PRODUCTION__ = ${serialize(production)}`
19 | }
20 |
21 | const cssChunks = {
22 | __html: `window.__CSS_CHUNKS__ = ${serialize(assets.cssHashRaw)}`
23 | }
24 | return (
25 |
26 |
27 |
28 | {MyHelmet.title}
29 | {MyHelmet.meta}
30 | {MyHelmet.link}
31 | {styles}
32 | {preload}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const compression = require('compression');
2 | const express = require('express');
3 | const morgan = require('morgan');
4 | const app = express();
5 | const path = require('path');
6 | const cacheTime = 86400000 * 30;
7 | const maxAge = { maxAge: cacheTime };
8 | app.disable('x-powered-by');
9 | app.use(morgan('combined'));
10 | app.use(compression());
11 | app.use('/js', express.static(path.join(__dirname, 'js'), maxAge));
12 | app.use('/vendor', express.static(path.join(__dirname, 'vendor'), maxAge));
13 | app.use('/icons', express.static(path.join(__dirname, 'icons'), maxAge));
14 | app.use('/css', express.static(path.join(__dirname, 'css'), maxAge));
15 | app.use('/html', express.static(path.join(__dirname, 'html'), maxAge));
16 | app.use('/flags', express.static(path.join(__dirname, 'flags'), maxAge));
17 | app.use('/images', express.static(path.join(__dirname, 'images'), maxAge));
18 | app.use('/appcache', express.static(path.join(__dirname, 'appcache'), maxAge));
19 | const clientStats = require('./stats.json');
20 | const outputPath = __dirname;
21 |
22 | app.get('/service-worker.js', (req, res) => {
23 | res.sendFile('./service-worker.js', {
24 | root: './',
25 | maxAge: cacheTime
26 | });
27 | });
28 |
29 | app.get('/manifest.json', (req, res) => {
30 | res.sendFile('/manifest.json', {
31 | root: './icons',
32 | maxAge: cacheTime
33 | });
34 | });
35 |
36 | app.get('/OneSignalSDKWorker.js', (req, res) => {
37 | res.sendFile('/OneSignalSDKWorker.js', {
38 | root: './signal',
39 | maxAge: 86400000 * 30
40 | });
41 | });
42 |
43 | app.get('/OneSignalSDKUpdaterWorker.js', (req, res) => {
44 | res.sendFile('/OneSignalSDKUpdaterWorker.js', {
45 | root: './signal',
46 | maxAge: 86400000 * 30
47 | });
48 | });
49 |
50 | app.use(
51 | require('./server/main').default({
52 | production: true,
53 | clientStats,
54 | outputPath
55 | })
56 | );
57 | app.listen(5000);
58 |
--------------------------------------------------------------------------------
/shared/images/boy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
--------------------------------------------------------------------------------
/store/createStore.tsx:
--------------------------------------------------------------------------------
1 | import { connectRoutes } from "redux-first-router"
2 | import { createStore, applyMiddleware, compose } from "redux"
3 | import { composeWithDevTools } from "redux-devtools-extension"
4 | import { createEpicMiddleware } from "redux-observable"
5 | import { RootEpic } from "./epics"
6 | import { allReducers } from "./reducers"
7 | import { logger } from "./logger"
8 | import { routesMap } from "../app/route.map"
9 | import * as storage from "redux-storage"
10 | import createEngine from "redux-storage-engine-localstorage"
11 | import debounce from "redux-storage-decorator-debounce"
12 | export let engine = createEngine("my-save-key")
13 | engine = debounce(engine, 3000)
14 | const ReplacebleEpicMiddleware = createEpicMiddleware(RootEpic)
15 |
16 | export const configureStore = (history: any = {}) => {
17 | const { reducer, middleware, enhancer } = connectRoutes(history, routesMap) // yes, 3 redux aspects
18 | const rootReducer = allReducers(reducer)
19 | const reducerXXX = storage.reducer(rootReducer)
20 | const middlewareXXX = storage.createMiddleware(engine, Object.keys(routesMap))
21 | const middlewares = composeWithDevTools(
22 | applyMiddleware(middleware,
23 | middlewareXXX,
24 | logger,
25 | ReplacebleEpicMiddleware
26 | )
27 | )
28 | const createStoreWithMiddleware = applyMiddleware(middlewareXXX)(createStore)
29 | const store = createStoreWithMiddleware(reducerXXX, compose(enhancer, middlewares))
30 | if (module.hot) {
31 | module.hot.accept(["./reducers.tsx"], () => {
32 | const nextRootReducer = require("./reducers.tsx").allReducers(reducer)
33 | store.replaceReducer(nextRootReducer)
34 | })
35 | module.hot.accept(["./epics.tsx"], () => {
36 | const nextRootEpic = require("./epics.tsx").RootEpic
37 | ReplacebleEpicMiddleware.replaceEpic(nextRootEpic)
38 | })
39 | }
40 |
41 | return store
42 | }
43 |
--------------------------------------------------------------------------------
/dobi.yaml:
--------------------------------------------------------------------------------
1 | meta:
2 | project: react-boil
3 | default: dev
4 |
5 |
6 | env=vars:
7 | variables:
8 | - FRONTEND_VERSION=0.0.1
9 |
10 | mount=source:
11 | bind: .
12 | path: /srv/react-boil
13 |
14 | image=dev-image:
15 | image: cescoferraro/react-boil
16 | tags: ["dev"]
17 | dockerfile: build/docker/Dockerfile.dev
18 | description: "Build the dev-image"
19 |
20 | image=frontend-image:
21 | image: cescoferraro/react-boil
22 | tags: ["{env.FRONTEND_VERSION}"]
23 | dockerfile: build/docker/Dockerfile.frontend
24 | depends: [frontend]
25 | description: "Build the frontend-image"
26 |
27 |
28 | alias=hub:
29 | description: Push Production Images to Docker Hub
30 | tasks: ["frontend-image:push"]
31 |
32 |
33 | job=frontend:
34 | description: "Runs Webpack Production"
35 | use: dev-image
36 | artifact: dist/app
37 | user: "{user.uid}"
38 | mounts: [source]
39 | interactive: true
40 | command: npm run production
41 | depends: [vars]
42 |
43 |
44 | job=delete:
45 | description: "delete dist/ folder"
46 | use: dev-image
47 | mounts: [source]
48 | command: rm -rf dist/
49 |
50 |
51 | job=dev:
52 | net-mode: host
53 | user: "{user.uid}"
54 | use: dev-image
55 | mounts: [source]
56 | interactive: true
57 | command: concurrently "node hot.js"
58 | depends: [vars]
59 | env:
60 | - "NODE_ENV=development"
61 |
62 |
63 | job=stage:
64 | net-mode: host
65 | user: "{user.uid}"
66 | use: dev-image
67 | mounts: [source]
68 | interactive: true
69 | command: node www/server.bundle.js
70 | depends: [vars,frontend]
71 | env:
72 | - "NODE_ENV=development"
73 |
74 |
75 | job=prod:
76 | net-mode: host
77 | user: "{user.uid}"
78 | use: dev-image
79 | mounts: [source]
80 | interactive: true
81 | command: node www/server.bundle.js
82 | depends: [vars,frontend]
83 | env:
84 | - "NODE_ENV=production"
85 |
--------------------------------------------------------------------------------
/internal/webpack/css.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const objectAssign = require('object-assign');
3 | const defaultOptions = { fileBlacklist: [/\.map/] };
4 | const serialize = require('serialize-javascript');
5 | const createCssHash = require('webpack-flush-chunks/dist/createApiWithCss')
6 | .createCssHash;
7 |
8 | /** Class representing a point. */
9 | class PreloadPlugin {
10 | /**
11 | * Create a point.
12 | * @param {options} options - The options.
13 | */
14 | constructor(options) {
15 | this.options = objectAssign({}, defaultOptions, options);
16 | }
17 |
18 | /**
19 | * Create a point.
20 | * @param {compiler} compiler - The webpack compiler.
21 | */
22 | apply(compiler) {
23 | let filesToInclude = '';
24 | let extractedChunks = [];
25 | compiler.plugin('compilation', compilation => {
26 | compilation.plugin(
27 | 'html-webpack-plugin-before-html-processing',
28 | (htmlPluginData, cb) => {
29 | extractedChunks = [...compilation.chunks.map(chunk => chunk.files)];
30 | const publicPath = compilation.outputOptions.publicPath || '';
31 | const hashes = createCssHash({
32 | publicPath,
33 | assetsByChunkName: extractedChunks
34 | });
35 | Object.keys(hashes).forEach(function(key) {
36 | const tt = hashes[key].split('_')[0].split('/').reverse()[0];
37 | hashes[tt] = hashes[key];
38 | delete hashes[key];
39 | });
40 | filesToInclude =
41 | ' ';
44 | if (htmlPluginData.html.indexOf('') !== -1) {
45 | htmlPluginData.html = htmlPluginData.html.replace(
46 | '',
47 | filesToInclude + ''
48 | );
49 | } else {
50 | htmlPluginData.html = htmlPluginData.html.replace(
51 | '',
52 | '' + filesToInclude + ''
53 | );
54 | }
55 | cb(null, htmlPluginData);
56 | }
57 | );
58 | });
59 | }
60 | }
61 |
62 | module.exports = PreloadPlugin;
63 |
--------------------------------------------------------------------------------
/app/components/user/item/__snapshots__/item.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render an Item 1`] = `
4 |
5 |
6 |
15 | }
16 | title="Email"
17 | value="teste@example.com"
18 | >
19 |
22 |
25 |
32 |
42 |
63 |
64 |
65 |
66 |
84 |
85 |
86 |
87 |
88 | `;
89 |
--------------------------------------------------------------------------------
/internal/webpack/loaders.js:
--------------------------------------------------------------------------------
1 | const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
2 |
3 | const SVG = {
4 | exclude: /node_modules/,
5 | loader: 'svg-react-loader',
6 | test: /\.svg$/
7 | };
8 |
9 | const postCSS = {
10 | loader: 'postcss-loader',
11 | options: {
12 | plugins: loader => [
13 | require('postcss-import')({ root: loader.resourcePath }),
14 | require('postcss-cssnext')({
15 | browsers: '> 0%',
16 | colorFunction: true,
17 | customProperties: true,
18 | customSelectors: true
19 | })
20 | ]
21 | }
22 | };
23 |
24 | const images = (client = false) => {
25 | return {
26 | test: /\.(gif|png|jpe?g)$/i,
27 | loaders: [
28 | {
29 | loader: 'file-loader',
30 | query: {
31 | emitFile: client,
32 | name: 'images/img-[sha512:hash:base64:7].[ext]'
33 | }
34 | },
35 | {
36 | loader: 'image-webpack-loader',
37 | query: {
38 | mozjpeg: {
39 | progressive: true
40 | },
41 | gifsicle: {
42 | interlaced: false
43 | },
44 | optipng: {
45 | optimizationLevel: 4
46 | },
47 | pngquant: {
48 | quality: '75-90',
49 | speed: 3
50 | }
51 | }
52 | }
53 | ]
54 | };
55 | };
56 |
57 | const typeScript = () => ({
58 | exclude: /node_modules/,
59 | use: [
60 | {
61 | loader: 'awesome-typescript-loader',
62 | options: {
63 | sourceMap: true,
64 | useBabel: false,
65 | useCache: true
66 | }
67 | }
68 | ],
69 | test: /\.tsx?$/
70 | });
71 |
72 | const cssHelper = client => {
73 | return {
74 | loader: client ? 'css-loader' : 'css-loader/locals',
75 | options: {
76 | localIdentName: '[name]__[local]--[hash:base64:5]',
77 | modules: true
78 | }
79 | };
80 | };
81 |
82 | const css = (client = false) => {
83 | const local = [cssHelper(client), postCSS];
84 | return {
85 | test: /\.css$/,
86 | use: client ? ExtractCssChunks.extract({ use: local }) : local
87 | };
88 | };
89 |
90 | const org = () => {
91 | return {
92 | test: /\.org/,
93 | loader: 'org-loader'
94 | };
95 | };
96 |
97 | const serverLoaders = env => ({
98 | rules: [images(), SVG, typeScript(), css(), org()]
99 | });
100 |
101 | const clientLoaders = env => ({
102 | rules: [images(true), SVG, typeScript(), css(true), org()]
103 | });
104 |
105 | module.exports = {
106 | clientLoaders: clientLoaders,
107 | serverLoaders: serverLoaders
108 | };
109 |
--------------------------------------------------------------------------------
/shared/images/girl.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
60 |
--------------------------------------------------------------------------------
/app/components/shell/index.tsx:
--------------------------------------------------------------------------------
1 | import Subheader from "material-ui/Subheader"
2 | import * as cs from "classnames"
3 | import * as React from "react"
4 | import AppBar from "material-ui/AppBar"
5 | import * as CSS from "./shell.css"
6 | import Drawer from "material-ui/Drawer"
7 | import MenuItem from "material-ui/MenuItem"
8 | const BoilLogo = require("../../../shared/images/boil.svg")
9 | const Cancel = require("../../../shared/images/cancel.svg")
10 | const Boy = require("../../../shared/images/boy.svg")
11 | const Girl = require("../../../shared/images/girl.svg")
12 | import ReduxToastr from "react-redux-toastr"
13 |
14 | export const Shell = (props) => {
15 | const closeOnClick = (open) => { props.DRAWER_ACTION(open) }
16 | const boilLogo = (
17 |
21 | )
22 | const goBack = () => {
23 | props.GO_HOME_ACTION()
24 | props.DRAWER_ACTION(false)
25 | }
26 | const goUser = (id) => () => {
27 | props.USER_ACTION(id)
28 | props.DRAWER_ACTION(false)
29 | }
30 | return (
31 |
32 |
38 |
39 | {props.children}
40 |
41 |
50 |
55 |
56 |
60 |
61 | React-boil
62 | }
64 | onClick={goUser(432)}
65 | >
66 | User 123
67 |
68 | }
70 | onClick={goUser(324)}
71 | >
72 | User 456
73 |
74 | }
77 | onClick={props.DRAWER_TOGGLE_ACTION}
78 | >
79 | Close
80 |
81 |
82 |
83 | )
84 | }
85 |
--------------------------------------------------------------------------------
/app/components/user/user.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as CSS from "./css/teste.css"
3 | import { Card } from "material-ui/Card"
4 | import Subheader from "material-ui/Subheader"
5 | import { ProfileClass } from "../../../store/reducers"
6 | import Code from "material-ui/svg-icons/action/code"
7 | import Terrain from "material-ui/svg-icons/maps/terrain"
8 | import LocationCity from "material-ui/svg-icons/social/location-city"
9 | import AccountCircle from "material-ui/svg-icons/action/account-circle"
10 | import GpsFixed from "material-ui/svg-icons/device/gps-fixed"
11 | import Divider from "material-ui/Divider"
12 | import { Item } from "./item/item"
13 | import { compose } from "recompose"
14 | import sizeMe from "react-sizeme"
15 | import Country from "./country/country"
16 |
17 | export class UserComponent extends React.Component {
18 | public render() {
19 | const profile: ProfileClass = new ProfileClass(this.props.profiles[this.props.profile.id])
20 | const grey = { fill: "grey" }
21 | return (
22 |
23 |
24 |
25 |

300 ? profile.picture.large : profile.picture.medium}
29 | width={150}
30 | />
31 |
34 |
35 | }
39 | />
40 | }
44 | />
45 | Address
46 | }
50 | />
51 | }
55 | />
56 | }
60 | />
61 | }
65 | />
66 |
67 |
68 |
69 |
70 | )
71 | }
72 | }
73 |
74 | export default compose(sizeMe())(UserComponent)
75 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | #+TITLE: React-boil
2 | #+OPTIONS: toc:nil
3 |
4 | Cutting edge PWA ready React.js Boilerplate. This is created as a
5 | template for my cookie cutter called [[http://github.com/cescoferraro/react-boiler][react-boiler]]. The goal here is
6 | to enable developers, a.k.a. me, deliver tomorrow's web today.
7 |
8 | | 👀 | [[https://github.com/facebook/react][react]] as the view |
9 | | 🔥 | [[http://redux.js.org/docs/introduction/][Redux]] store ready! All the cool tooling and extensions. |
10 | | 🔀 | [[https://github.com/faceyspacey/redux-first-router][redux-first-router]] as the router. |
11 | | 🚄 | [[https://expressjs.com/][express]] server. |
12 | | 🎛 | Offilne first. Try going offline for a bit. |
13 | | 🔥 | [[https://onesignal.com][OneSignal]] gives us web push Notifications to keep in touch with our customers |
14 | | 🖌 | [[https://github.com/michaelcontento/redux-storage][ReduxStorage]] makes the app remember where the user was last time with ALL THE DATA! Native app experience |
15 | | 🔥 | [[https://firebase.google.com/][Firebase]] totally integrated Redux store ready! Google Infrastructure ready to ship! |
16 | | 🚀 | [[https://github.com/redux-observable/redux-observable][Redux-Observable]] to perform asynchronous redux actions. |
17 | | 🌍 | [[https://facebook.github.io/react/docs/react-dom-server.html][Server Side Rendering]] because its 2017. |
18 | | 💩 | [[https://github.com/facespacey/webpack-flush-chunks][webpack-flush-chunks]] framework less code splitting & SSR FULL CONTROL |
19 | | 🖌 | [[https://github.com/postcss/postcss][PostCSS]] lets you use next css syntax now. |
20 | | 👼 | [[https://github.com/kriasoft/isomorphic-style-loader][Extract-Css-Chunks]] injects only css critical path to the website. |
21 | | 📦 | All source is bundled using [[https://webpack.js.org/configuration/][Webpack v3]]. |
22 | | 🔥 | Extreme live development using [[https://webpack.js.org/configuration/][webpack]] standard tooling like [[https://github.com/60frames/webpack-hot-server-middleware][webpack-hot-server-middleware]] & [[https://github.com/glenjamin/webpack-hot-middleware][webpack-hot-middleware]]. |
23 | | 🍃 | Tree-shaking, courtesy of Webpack. |
24 | | 🎛 | Pre-configured to support development and optimized production builds. |
25 | | 🤖 | [[https://github.com/webpack/docs/wiki/list-of-plugins#dllplugin][Vendor DLL]] for smooth development experiences. |
26 | | 💩 | Code splitting Thanks to [[https://www.npmjs.com/package/react-universal-component][react-universal-component]] |
27 | | 😎 | Progressive Web Application ready, with offline support, via a Service Worker. |
28 | | 🕸 | Is this reality? Believe at the web https://boil.cescoferraro.xyz |
29 | | 🎭 | `jest` as the test framework. Snapshot for life!
30 |
31 |
32 |
33 |
34 | # LocalWords: Pre redux PWA js SSR css LocalWords
35 |
--------------------------------------------------------------------------------
/server/helpers.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Helmet } from "react-helmet"
3 |
4 | const CachedFs = require("cachedfs")
5 | const fs = new CachedFs()
6 |
7 | const path = require("path")
8 |
9 | declare module "react" {
10 | /* tslint:disable */
11 | interface HTMLAttributes extends React.DOMAttributes {
12 | /* tslint:enable */
13 | as?: string
14 | }
15 | }
16 |
17 | export const BaseStyle = () => (
18 |
21 | )
22 |
23 | export const OneSignalCDN = ({ production }) => {
24 | return (
25 | production ?
26 | () :
27 | null)
28 | }
29 | export const OneSignalInit = ({ production }) => {
30 | const text = ` var OneSignal = window.OneSignal || []; ` +
31 | `OneSignal.push(["init", {` +
32 | `appId: "396fb320-f5e2-4ed2-af88-efbee83b2564", ` +
33 | `autoRegister: true, notifyButton: {enable: true}}]); `
34 | return production ?
35 | (
36 |
37 | ) :
38 | null
39 | }
40 | export const Dll = ({ outputPath }) => {
41 | const place = path.resolve(outputPath, "../dll/vendor.dll.js")
42 | console.log(fs.existsSync(place) ? "using dll" : " not using dll")
43 | return fs.existsSync(place) ?
44 | :
45 | null
46 | }
47 | export const App = ({ content }) => (
48 |
52 | )
53 | export const Javascript = ({ scripts }) => ({scripts})
54 | export const Manifest = ({ production }) => {
55 | return production ? : null
56 | }
57 | export const getScripts = (scripts: string[], outputPath, production) => {
58 | return scripts.reduce(
59 | (acc, script: string) => {
60 | const scriptPath = `/${script}`
61 | const place = path.join(outputPath, scriptPath)
62 |
63 | let preload = (
64 |
70 | )
71 |
72 | let scriptTag = (
73 |
78 | )
79 | if (production) {
80 | if (place.includes("bootstrap")) {
81 | scriptTag = (
82 |
86 | )
87 | preload = null
88 | }
89 | }
90 |
91 | return {
92 | preload: [...acc.preload, preload],
93 | scripts: [...acc.scripts, scriptTag]
94 | }
95 | },
96 | {
97 | preload: [],
98 | scripts: []
99 | }
100 | )
101 | }
102 |
103 | export const getStyles = (styles: string[], outputPath, production) => {
104 | return styles.map((style: string) => {
105 | const stylePath = `/${style}`
106 | const place = outputPath + stylePath
107 | return production ? (
108 |
111 | ) : (
112 |
121 | )
122 | })
123 | }
124 |
125 | export const Helmator = () => {
126 | const HelmetApp = Helmet.renderStatic()
127 | return ({
128 | html: HelmetApp.htmlAttributes.toComponent(),
129 | body: HelmetApp.bodyAttributes.toComponent(),
130 | title: HelmetApp.title.toComponent(),
131 | meta: HelmetApp.meta.toComponent(),
132 | link: HelmetApp.link.toComponent()
133 | })
134 | }
135 |
--------------------------------------------------------------------------------
/internal/webpack/plugins.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const FaviconsWebpackPlugin = require('favicons-webpack-plugin-cesco');
3 | const webpack = require('webpack');
4 | const ExtractCssChunks = require('extract-css-chunks-webpack-plugin');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 | const StatsPlugin = require('stats-webpack-plugin');
7 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
8 | .BundleAnalyzerPlugin;
9 | const noop = require('noop-webpack-plugin');
10 | const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
11 | const Harmony = require('uglifyjs-webpack-plugin');
12 | const PreloadWebpackPlugin = require('./css.js');
13 | // const ImageminPlugin = require('imagemin-webpack-plugin').default;
14 |
15 | const icons = new FaviconsWebpackPlugin({
16 | config: {
17 | appName: 'react-boil',
18 | display: ' fullscreen',
19 | lang: 'pt',
20 | start_url: '/',
21 | statsFilename: 'icons/[name].[ext]',
22 | gcm_sender_id: '482941778795',
23 | theme_color: '#00bfff'
24 | },
25 | emitStats: true,
26 | logo: './shared/icon/favicon.png',
27 | persistentCache: false,
28 | prefix: 'icons/'
29 | });
30 |
31 | const Uglify = new Harmony();
32 |
33 | const SW = new SWPrecacheWebpackPlugin({
34 | directoryIndex: 'html/index.html'
35 | });
36 |
37 | const HTML = [
38 | new HtmlWebpackPlugin({
39 | chunks: ['bootstrap', 'vendor', 'main'],
40 | chunksSortMode: (a, b) => {
41 | const order = ['bootstrap', 'vendor', 'main'];
42 | return order.indexOf(a.names[0]) - order.indexOf(b.names[0]);
43 | },
44 | filename: 'html/index.html',
45 | inject: true,
46 | showErrors: true,
47 | template: './server/index.html'
48 | }),
49 | new PreloadWebpackPlugin()
50 | ];
51 |
52 | const limit = () =>
53 | new webpack.optimize.LimitChunkCountPlugin({
54 | maxChunks: 1
55 | });
56 |
57 | const flags = () => {
58 | return new CopyWebpackPlugin([
59 | {
60 | from: './node_modules/react-flags/vendor/flags',
61 | to: './flags'
62 | }
63 | ]);
64 | };
65 |
66 | const copy = () => [
67 | new CopyWebpackPlugin(
68 | [
69 | { from: './server/server.js', to: './server.js' },
70 | {
71 | from: './shared/signal/OneSignalSDKUpdaterWorker.js',
72 | to: './signal/OneSignalSDKUpdaterWorker.js'
73 | },
74 | {
75 | from: './shared/signal/OneSignalSDKWorker.js',
76 | to: './signal/OneSignalSDKWorker.js'
77 | },
78 | { from: './node_modules/react-flags/vendor/flags', to: './flags' }
79 | ],
80 | { copyUnmodified: true }
81 | ),
82 | flags()
83 | // new ImageminPlugin({ test: /\.(jpe?g|png|gif|svg)$/i })
84 | ];
85 |
86 | const define = env =>
87 | new webpack.DefinePlugin({
88 | 'process.env': {
89 | NODE_ENV: JSON.stringify(env.production ? 'production' : 'development')
90 | }
91 | });
92 |
93 | const common = () =>
94 | new webpack.optimize.CommonsChunkPlugin({
95 | filename: 'js/[name]_[hash].js',
96 | minChunks: Infinity,
97 | names: ['bootstrap']
98 | });
99 |
100 | const vendor = () =>
101 | new webpack.optimize.CommonsChunkPlugin({
102 | chunks: ['main', 'vendorC'],
103 | filename: 'js/vendor.js',
104 | name: 'vendor'
105 | });
106 | const stats = () => {
107 | return new StatsPlugin('stats.json');
108 | };
109 | const analyzer = env => {
110 | return env.analyzer ? new BundleAnalyzerPlugin() : noop();
111 | };
112 | const hmr = () => {
113 | return new webpack.HotModuleReplacementPlugin();
114 | };
115 | const dllReference = () => {
116 | return new webpack.DllReferencePlugin({
117 | context: __dirname,
118 | manifest: require('../../dll/vendor.dll.json'),
119 | name: 'react'
120 | });
121 | };
122 | const cssProd = () => {
123 | return new ExtractCssChunks({ filename: 'css/[name]_[hash].css' });
124 | };
125 | const cssDev = () => {
126 | return new ExtractCssChunks();
127 | };
128 |
129 | const clientPlugins = env => {
130 | if (env.production) {
131 | return [
132 | icons,
133 | stats(),
134 | cssProd(),
135 | common(),
136 | define(env),
137 | analyzer(env),
138 | vendor(),
139 | SW,
140 | ...HTML,
141 | Uglify
142 | ];
143 | } else {
144 | return [
145 | icons,
146 | dllReference(),
147 | hmr(),
148 | cssDev(),
149 | common(),
150 | new webpack.NoEmitOnErrorsPlugin(),
151 | define(env),
152 | SW
153 | ];
154 | }
155 | };
156 |
157 | const serverPlugins = env => {
158 | if (env.production) {
159 | return [...copy(), limit(), define(env), Uglify];
160 | } else {
161 | return [flags(), limit(), define(env)];
162 | }
163 | };
164 |
165 | module.exports = {
166 | clientPlugins: clientPlugins,
167 | serverPlugins: serverPlugins
168 | };
169 |
--------------------------------------------------------------------------------
/store/reducers.tsx:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux"
2 | import { reducer as toastrReducer } from "react-redux-toastr"
3 |
4 | import { NOT_FOUND } from "redux-first-router"
5 | import { DRAWER_ACTION_NAME, DRAWER_TOGGLE_ACTION_NAME } from "./actions"
6 | export const userIdReducer = (state = null, action: any = {}) => {
7 | switch (action.type) {
8 | case "HOME":
9 | case NOT_FOUND:
10 | return null
11 | case "USER":
12 | return action.payload.id
13 | case "USER_FOUND":
14 | return action.payload.user.id
15 | default:
16 | return state
17 | }
18 | }
19 | interface ILocation {
20 | city: string
21 | postcode: number
22 | state: string
23 | street: string
24 | }
25 | interface ILogin {
26 | md5: string
27 | password: string
28 | salt: string
29 | sha1: string
30 | sha256: string
31 | username: string
32 |
33 | }
34 | interface IName {
35 | first: string
36 | last: string
37 | title: string
38 | }
39 | interface IPicture {
40 | thumbnail: string
41 | medium: string
42 | large: string
43 | }
44 |
45 | export interface IProfile {
46 | cell: string
47 | dob: string
48 | email: string
49 | gender: string
50 | id: number
51 | location: ILocation
52 | login: ILogin
53 | name: IName
54 | nat: string
55 | phone: string
56 | registered: string
57 | picture: IPicture
58 | }
59 |
60 | function capitalizeFirstLetter(text) {
61 | return text.charAt(0).toUpperCase() + text.slice(1)
62 | }
63 | export class ProfileClass implements IProfile {
64 | public cell: string
65 | public dob: string
66 | public email: string
67 | public gender: string
68 | public id: number
69 | public location: ILocation
70 | public login: ILogin
71 | public name: IName
72 | public nat: string
73 | public phone: string
74 | public registered: string
75 | public picture: IPicture
76 | constructor(obj: IProfile) {
77 | this.cell = obj.cell
78 | this.dob = obj.dob
79 | this.email = obj.email
80 | this.gender = obj.gender
81 | this.id = obj.id
82 | this.location = obj.location
83 | this.login = obj.login
84 | this.name = obj.name
85 | this.nat = obj.nat
86 | this.phone = obj.phone
87 | this.registered = obj.registered
88 | this.picture = obj.picture
89 | }
90 | public fullname() {
91 | return capitalizeFirstLetter(this.name.title) +
92 | " " +
93 | capitalizeFirstLetter(this.name.first) +
94 | " " +
95 | capitalizeFirstLetter(this.name.last)
96 | }
97 | }
98 | export const profileStartup: IProfile = {
99 | cell: "(057)-526-3510",
100 | dob: "1986-08-29 16:48:32",
101 | email: "gabriel.mathieu@example.com",
102 | gender: "male",
103 | id: 123,
104 | location:
105 | {
106 | city: "pully",
107 | postcode: 8831,
108 | state: "graubünden",
109 | street: "2976 quai charles-de-gaulle"
110 | },
111 |
112 | login: {
113 | md5: "07ad5b07047398a92de1ec6a9b65f7af",
114 | password: "leonardo",
115 | salt: "zYa8DyXr",
116 | sha1: "0a4d1a3a4eae579dfa25a0f2e5fd9a265d7e1cd0",
117 | sha256: "397fde48397adf6f350642cad79ed8f41a4ff0e22c3007fc90537d9c394141f0",
118 | username: "crazymouse692"
119 | },
120 | name: {
121 | first: "John",
122 | last: "Doe",
123 | title: "Mr."
124 | },
125 | nat: "BR",
126 | phone: "(009)-085-3186",
127 | picture: {
128 | large: "http://via.placeholder.com/700x700",
129 | medium: "http://via.placeholder.com/300x300",
130 | thumbnail: "http://via.placeholder.com/150x150"
131 | },
132 | registered: "2013-02-04 00:12:29"
133 | }
134 | export const profileReducer = (state = profileStartup, action: any = {}) => {
135 | switch (action.type) {
136 | case "USER_FOUND":
137 | return action.payload.user
138 | default:
139 | return state
140 | }
141 | }
142 | export const drawer = (state = false, action: any = {}) => {
143 | switch (action.type) {
144 | case DRAWER_TOGGLE_ACTION_NAME:
145 | return !state
146 | case DRAWER_ACTION_NAME:
147 | return action.payload
148 | default:
149 | return state
150 | }
151 | }
152 |
153 | export const profilesReducer = (state = { 123: profileStartup }, action: any = {}) => {
154 | switch (action.type) {
155 | case "USER_FOUND":
156 | const Newer = state
157 | Newer[action.payload.user.id] = action.payload.user
158 | return Newer
159 | default:
160 | return state
161 | }
162 | }
163 | export let allReducers = (location) => combineReducers({
164 | location,
165 | toastr: toastrReducer,
166 | userId: userIdReducer,
167 | profile: profileReducer,
168 | profiles: profilesReducer,
169 | drawer
170 | })
171 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "React-Boil",
3 | "version": "1.0.0",
4 | "description": "Cutting edge PWA React.js boilerplate.",
5 | "main": "hot.js",
6 | "scripts": {
7 | "dll": "webpack --config webpack.dll.js ",
8 | "clear": "rm -rf dll/ dist/",
9 | "clean": "rm -rf dist/",
10 | "dev": "node hot.js",
11 | "start": "yarn run clear && yarn run dll && yarn run ",
12 | "client": "webpack --config webpack.client.js --env.production",
13 | "server": "webpack --config webpack.server.js --env.production",
14 | "prod": "webpack --config webpack.config.js --env.production",
15 | "preview": "yarn run clear && yarn run prod && cd dist/ && node server.js",
16 | "analyzer": "webpack --config webpack.client.js --env.production --env.analyzer",
17 | "test": "jest --coverage",
18 | "lint": "tslint --type-check --project tsconfig.json",
19 | "docker/build": "docker build -f internal/docker/Dockerfile -t cescoferraro/boil:latest .",
20 | "docker/run": "docker run -p 4000:4000 -v dist/:/srv/boil cescoferraro/boil:latest",
21 | "docker/push": "docker push cescoferraro/boil:latest",
22 | "docker/preview": "yarn run clean && yarn build && yarn docker/run",
23 | "push": "yarn run clean && yarn run prod && yarn run docker/build && yarn run docker/push",
24 | "webpack-defaults": "webpack-defaults"
25 | },
26 | "author": "Francesco Antonello Ferraro",
27 | "license": "ISC",
28 | "dependencies": {
29 | "@types/debug": "^0.0.29",
30 | "@types/jest": "^20.0.5",
31 | "@types/material-ui": "^0.17.19",
32 | "@types/offline-js": "^0.7.28",
33 | "@types/react": "^15.0.35",
34 | "@types/react-redux": "^4.4.45",
35 | "awesome-typescript-loader": "^3.2.2",
36 | "babel-plugin-universal-import": "^1.2.3",
37 | "cachedfs": "^0.3.3",
38 | "classnames": "^2.2.5",
39 | "compression": "^1.7.0",
40 | "copy-webpack-plugin": "^4.0.1",
41 | "core-js": "^2.4.1",
42 | "css-loader": "^0.28.4",
43 | "enzyme": "^2.9.1",
44 | "enzyme-to-json": "^1.5.1",
45 | "eslint-config-cesco": "^0.0.2",
46 | "express": "^4.15.3",
47 | "extract-css-chunks-webpack-plugin": "^2.0.16",
48 | "extract-text-webpack-plugin": "^2.1.2",
49 | "faker": "^4.1.0",
50 | "favicons-webpack-plugin-cesco": "^0.0.6",
51 | "file-loader": "^0.11.2",
52 | "global": "^4.3.2",
53 | "history": "^4.6.3",
54 | "html-webpack-plugin": "^2.30.1",
55 | "identity-obj-proxy": "^3.0.0",
56 | "image-webpack-loader": "^3.3.1",
57 | "jest": "^20.0.4",
58 | "localforage": "^1.5.0",
59 | "lodash": "^4.17.4",
60 | "material-ui": "^0.18.7",
61 | "morgan": "^1.8.2",
62 | "noop-webpack-plugin": "^1.0.1",
63 | "offline-js": "^0.7.19",
64 | "org": "^0.2.0",
65 | "org-loader": "^1.0.1",
66 | "postcss-cssnext": "^3.0.2",
67 | "postcss-import": "^10.0.0",
68 | "postcss-loader": "^2.0.6",
69 | "rapscallion": "^2.1.13",
70 | "raw-loader": "^0.5.1",
71 | "react": "^15.5.4",
72 | "react-dom": "^15.5.4",
73 | "react-flag-icon-css": "^1.0.19",
74 | "react-flags": "wiredmax/react-flags#9/head",
75 | "react-helmet": "^5.1.3",
76 | "react-hot-loader": "3.0.0-beta.6",
77 | "react-redux": "^5.0.5",
78 | "react-redux-firebase": "1.5.0-beta.3",
79 | "react-redux-toastr": "^7.1.0",
80 | "react-sizeme": "^2.3.4",
81 | "react-tap-event-plugin": "^2.0.1",
82 | "react-test-renderer": "^15.6.1",
83 | "react-universal-component": "^2.0.22",
84 | "react-world-flags": "^0.0.3",
85 | "recompose": "^0.24.0",
86 | "redux": "^3.7.2",
87 | "redux-devtools-extension": "^2.13.2",
88 | "redux-first-router": "^1.9.8",
89 | "redux-first-router-link": "^1.1.3",
90 | "redux-logger": "^3.0.6",
91 | "redux-observable": "^0.14.1",
92 | "redux-storage": "^4.1.2",
93 | "redux-storage-decorator-debounce": "^1.1.3",
94 | "redux-storage-engine-localstorage": "^1.1.4",
95 | "rxjs": "^5.4.2",
96 | "serialize-javascript": "^1.4.0",
97 | "source-map-loader": "^0.2.1",
98 | "stats-webpack-plugin": "^0.6.1",
99 | "svg-react-loader": "^0.4.4",
100 | "svg-to-react": "^0.1.0",
101 | "sw-precache-webpack-plugin": "^0.11.4",
102 | "ts-jest": "^20.0.7",
103 | "tslint": "^5.5.0",
104 | "tslint-react": "^3.2.0",
105 | "typed-css-modules-loader": "^0.0.10",
106 | "typescript": "^2.4.2",
107 | "uglifyjs-webpack-plugin": "v1.0.0-beta.1",
108 | "webpack": "^3.4.1",
109 | "webpack-bundle-analyzer": "^2.8.3",
110 | "webpack-dev-middleware": "^1.12.0",
111 | "webpack-flush-chunks": "^1.1.17",
112 | "webpack-hot-middleware": "^2.18.2",
113 | "webpack-hot-server-middleware": "^0.1.0",
114 | "xmlhttprequest": "^1.8.0"
115 | },
116 | "devDependencies": {
117 | "eslint": "^4.3.0"
118 | },
119 | "jest": {
120 | "transform": {
121 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js"
122 | },
123 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
124 | "moduleFileExtensions": [
125 | "ts",
126 | "tsx",
127 | "js"
128 | ],
129 | "setupFiles": [
130 | "/internal/test/jestSetup.js"
131 | ],
132 | "snapshotSerializers": [
133 | "/node_modules/enzyme-to-json/serializer"
134 | ],
135 | "moduleNameMapper": {
136 | "\\.(jpg|jpeg|png|gif|svg|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/internal/test/jest.js",
137 | "\\.(css)$": "identity-obj-proxy"
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/shared/images/boil.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
91 |
--------------------------------------------------------------------------------
/app/components/user/__snapshots__/user.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Render a User 1`] = `
4 |
7 |
11 |
14 |
17 |

23 |
30 |
31 |
61 |
91 |
94 | Address
95 |
96 |
127 |
158 |
189 |
220 |
223 |
226 |

231 |
232 |
246 |
247 |
250 |
251 |
252 |
253 | `;
254 |
--------------------------------------------------------------------------------
/server/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
15 | React-boil
16 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------