Skip to content

Commit

Permalink
feat: create base files
Browse files Browse the repository at this point in the history
  • Loading branch information
weyheyhey committed Sep 16, 2018
0 parents commit 8c19be1
Show file tree
Hide file tree
Showing 56 changed files with 9,946 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-react", "@babel/preset-env"]
}
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[*]
charset=utf-8
end_of_line = lf
insert_final_newline=true
indent_style=space
indent_size=2

[*.md]
max_line_length = off
trim_trailing_whitespace = false
insert_final_newline = false
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SSR_CACHE=false
SSR_CACHE_TIME=3600000
HOST=localhost
PROD_PORT=8081
DEV_PORT=3001
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# compiled output
/dist
/tmp
/out-tsc

# dependencies
/node_modules
/storybook-static

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
testem.log

# System Files
.DS_Store
Thumbs.db
11 changes: 11 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"tabWidth": 2,
"singleQuote": true,
"useTabs": false,
"semi": true,
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"proseWrap": "always",
"printWidth": 120
}
6 changes: 6 additions & 0 deletions .storybook/addons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import '@storybook/addon-knobs/register';
import '@storybook/addon-links/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-options/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-backgrounds/register';
25 changes: 25 additions & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { withBackgrounds } from '@storybook/addon-backgrounds';
import { addDecorator, configure } from '@storybook/react';
import { setOptions } from '@storybook/addon-options';
import { withKnobs } from '@storybook/addon-knobs';

import { reactDecorator } from './reactDecorator';

addDecorator(
withBackgrounds([
{ name: 'Gray', value: '#252525', default: true },
{ name: 'Base', value: '#ffffff' },
{ name: 'Black', value: '#000' }
])
);

addDecorator(withKnobs);

addDecorator(reactDecorator);

setOptions({ name: 'Storybook' });

configure(() => {
const req = require.context('../client', true, /\.story\.tsx?$/);
req.keys().forEach(req);
}, module);
28 changes: 28 additions & 0 deletions .storybook/reactDecorator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { Provider } from 'react-redux';
import { createMemoryHistory } from 'history';
import { ConnectedRouter } from 'connected-react-router';

import { configureStore } from '../client/common/store';

import 'assets/styles/global.scss';

const history = createMemoryHistory();

const store = configureStore(history);

const wrapStyles = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100vh',
width: '100%'
};

export const reactDecorator = story => (
<Provider store={store}>
<ConnectedRouter history={history}>
<div style={wrapStyles}>{story()}</div>
</ConnectedRouter>
</Provider>
);
30 changes: 30 additions & 0 deletions .storybook/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const OpenBrowserPlugin = require('open-browser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const merge = require('webpack-merge');
const { dissoc } = require('ramda');
const path = require('path');

const webpackConfig = path.resolve(__dirname, '..', 'webpack.config');
const { node, target, resolve, module: webpackModule } = require(webpackConfig);

module.exports = (baseConfig, env, defaultConfig) =>
merge(dissoc('module', defaultConfig), {
node,
target,
resolve,
module: webpackModule,
plugins: [
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new OpenBrowserPlugin({ url: `http://localhost:6006` })
],
devServer: {
hot: true,
inline: true,
overlay: true,
compress: true,
stats: 'minimal',
historyApiFallback: {
disableDotRule: true
}
}
});
24 changes: 24 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# React TypeScript Express SSR boilerplate

Ready to grow boilerplate with css-modules, connected-react-router, react-router, redux, redux-thunk, ramda, webpack 4.

You can read more about the organizational strategy used in this app in
[this Medium post](https://medium.com/@nate_wang/feature-oriented-architecture-for-web-applications-2b48e358afb0), or
[this post](https://jaysoo.ca/2016/02/28/organizing-redux-application/).

## Contains

- [x] [React](https://facebook.github.io/react/) 16.5
- [x] [React Router](https://github.com/ReactTraining/react-router) 4.3
- [x] [Redux](https://github.com/reactjs/redux) 4
- [x] [Redux-Thunk](https://github.com/gaearon/redux-thunk) 2.3
- [x] [connected-react-router](https://github.com/supasate/connected-react-router) 4.4
- [x] [storybook](https://github.com/storybooks/storybook) 4.0.0-alpha.21
- [x] [Ramda](https://github.com/ramda/ramda) 0.2
- [x] [express](https://github.com/expressjs/express) 4.16
- [x] [webpack](https://github.com/webpack/webpack-dev-middleware) 4.19
- [x] [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) 3.3
- [x] [webpack-hot-middleware](https://github.com/webpack-contrib/webpack-hot-middleware) 2.24
- [x] [babel](https://github.com/babel/babel) 7

## Setup

```
$ yarn
```

## Running

Start express development server

```
$ yarn start
```

## Build

Build client for production

```
$ yarn build:client
```

Build server for production

```
$ yarn build:server
```

Or simply

```
$ yarn build
```

Build server & client for production

## Storybook

Start storybook

```
$ yarn storybook
```

Build storybook

```
$ yarn build-storybook
```

## Analyze the Bundle Size

```
$ yarn analyze
```
9 changes: 9 additions & 0 deletions client/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { RequestApi } from './request';

export interface AppApi {
requestApi: RequestApi;
}

export const initializeApi = (fetchUrl: string): AppApi => ({
requestApi: new RequestApi(fetchUrl)
});
32 changes: 32 additions & 0 deletions client/api/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export class RequestApi {
constructor(private baseUrl: string, private defaultOptions?: Object) {}

public get(url: string, options?: Object) {
return this.createRequest('GET', url, options);
}

public post(url: string, body?: Object, options?: Object) {
const fullOptions = Object.assign({}, { body: JSON.stringify(body) }, options);
return this.createRequest('POST', url, fullOptions);
}

public put(url: string, body?: Object, options?: Object) {
const fullOptions = Object.assign({}, { body: JSON.stringify(body) }, options);
return this.createRequest('PUT', url, fullOptions);
}

public patch(url: string, body?: Object, options?: Object) {
const fullOptions = Object.assign({}, { body: JSON.stringify(body) }, options);
return this.createRequest('PATCH', url, fullOptions);
}

public delete(url: string, body?: Object, options?: Object) {
const fullOptions = Object.assign({}, { body: JSON.stringify(body) }, options);
return this.createRequest('DELETE', url, fullOptions);
}

private createRequest(method: string, url?: string, options?: Object) {
const fullOptions = Object.assign({ method }, this.defaultOptions, options);
return fetch(`${this.baseUrl}${url}`, fullOptions).then(res => res.json());
}
}
Empty file added client/assets/styles/fonts.scss
Empty file.
11 changes: 11 additions & 0 deletions client/assets/styles/global.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import './variables';
@import './mixins';
@import './fonts';

html,
body {
width: 100%;
height: 100%;
font-family: sans-serif;
line-height: 1;
}
Empty file.
Empty file.
35 changes: 35 additions & 0 deletions client/common/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { merge } from 'ramda';

export interface AppConfig {
evn: string;
host: string;
port: number;
isDev: boolean;
apiUrl: string;
socketUrl: string;
isBrowser: boolean;
translateUrl: string;
}

// tslint:disable: no-string-literal

const defaultConfig = {
common: {
env: process.env.NODE_ENV || 'development',
isDev: process.env.NODE_ENV !== 'production',
isBrowser: process && process['browser'],
basename: '/'
},
development: {
host: process.env.HOST || 'localhost',
port: process.env.DEV_PORT || 3001,
apiUrl: ``
},
production: {
host: process.env.HOST || 'localhost',
port: process.env.PROD_PORT || 8081,
apiUrl: ``
}
};

export const config: AppConfig = merge(defaultConfig.common, defaultConfig[defaultConfig.common.env]);
10 changes: 10 additions & 0 deletions client/common/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ThunkAction } from 'redux-thunk';

import { AppApi } from 'api';
import { AppState } from 'common/reducer';

// tslint:disable: no-any
export type PreloadAction = (...args: any[]) => ThunkAction<Promise<any>, AppState, AppApi>;

// Экшены, которые необходимо диспатчить на каждом роуте (SSR)
export const globalPreloadActions: PreloadAction[] = [];
8 changes: 8 additions & 0 deletions client/common/reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { RouterState } from 'connected-react-router';
import { combineReducers } from 'redux';

export interface AppState {
router: RouterState;
}

export const reducer = combineReducers<AppState>({});
Loading

0 comments on commit 8c19be1

Please sign in to comment.