Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client dependency updates, move to TypeScript #47

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<img src="/resources/preview-thumbnail.jpg" />
</p>

A starting point for building universal/isomorphic React applications with ASP.NET Core 1, leveraging existing front-end approaches. Uses the [JavaScriptViewEngine](https://github.com/pauldotknopf/javascriptviewengine).
A starting point for building universal/isomorphic React applications with ASP.NET Core 2, leveraging existing front-end approaches. Uses the [JavaScriptViewEngine](https://github.com/pauldotknopf/javascriptviewengine).

## Goals

1. **Minimize .NET's usage** - It's only usage should be for building REST endpoints (WebApi) and providing the initial state (pure POCO). No razor syntax *anywhere*.
2. **Isomorphic/universal rendering**
3. **Client and server should render using the same source files (javascript)**
3. **Client and server should render using the same source files (TypeScript)**
4. **Out-of-the-box login/register/manage functionality** - Use the branch ```empty-template``` if you wish to have a vanilla React application.

This approach is great for front-end developers because it gives them complete control to build their app as they like. No .NET crutches (bundling/razor). No opinions. No gotchas. Just another typical React client-side application, but with the initial state provided by ASP.NET for each URL.
Expand Down Expand Up @@ -40,7 +40,7 @@ After you have your new project generated, let's run that app!
```bash
cd src/ReactBoilerplate
npm install
gulp
gulp build
dotnet restore
# The following two lines are only for the 'master' branch, which has a database backend (user management).
# They are not needed when using 'empty-template'.
Expand All @@ -49,15 +49,30 @@ dotnet ef database update
dotnet run
```

Additionally, other gulp actions are available to aid in development.

Build bundles and server-side code in production mode:
```bash
gulp prod build
```
Build and watch both the bundles and the server-side code (in development mode), and run a browsersync proxy to serve both:
```bash
gulp start
```

Build both the bundles and the server-side code (in production mode), and execute "dotnet run" to serve both:
```bash
gulp prod start
```

Some of the branches in this repo that are maintained:
* [```master```](https://github.com/pauldotknopf/react-aspnet-boilerplate/tree/master) - This is the main branch. It has all the stuff required to get you started, including membership, external logins (OAuth) and account management. This is the default branch used with the Yeoman generator.
* [```empty-template```](https://github.com/pauldotknopf/react-aspnet-boilerplate/tree/empty-template) - This branch for people that want an empty template with the absolute minimum recommend boilerplate for any ASP.NET React application.

## The interesting parts

- [client.js](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/client.js) and [server.js](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/server.js) - The entry point for the client-side/server-side applications.
- [Html.js](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/helpers/Html.js) and [App.js](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/containers/App/App.js) - These files essentially represent the "React" version of MVC Razor's "_Layout.cshtml".
- [client.tsx](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/client.tsx) and [server.tsx](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/server.tsx) - The entry point for the client-side/server-side applications.
- [Html.tsx](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/helpers/Html.tsx) and [App.tsx](https://github.com/pauldotknopf/react-dot-net/blob/master/src/ReactBoilerplate/Scripts/containers/App/App.tsx) - These files essentially represent the "React" version of MVC Razor's "_Layout.cshtml".
- [Controllers](https://github.com/pauldotknopf/react-aspnet-boilerplate/tree/master/src/ReactBoilerplate/Controllers) - The endpoints for a each initial GET request, and each client-side network request.

## What is next?
Expand Down
4 changes: 0 additions & 4 deletions src/ReactBoilerplate/.babelrc

This file was deleted.

23 changes: 15 additions & 8 deletions src/ReactBoilerplate/.bootstraprc
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
{
"bootstrapVersion": 3,
bootstrapVersion: 3

"useFlexbox": true,
"extractStyles": true,
"styleLoaders": ["style", "css", "sass"],
env:
development:
extractStyles: false
styleLoaders:
- style-loader
- css-loader
- sass-loader
production:
extractStyles: true
styleLoaders:
- css-loader
- sass-loader

"styles": true,
styles: true

"scripts": false
}
scripts: false
4 changes: 4 additions & 0 deletions src/ReactBoilerplate/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ root = true
indent_style = space
indent_size = 2

[*.cs]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false
81 changes: 61 additions & 20 deletions src/ReactBoilerplate/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,76 @@
"browser": true,
"node": true
},
"parser": "babel-eslint",
"parser": "typescript-eslint-parser",
"plugins": [
"react",
"import"
"import",
"typescript"
],
"rules": {
"comma-dangle": 0, // not sure why airbnb turned this on. gross!
"indent": [2, 2, {"SwitchCase": 1}],
"react/prefer-stateless-function": 0,
"react/prop-types": 0,
"react/jsx-closing-bracket-location": 0,
"no-console": 0,
"prefer-template": 0,
"max-len": 0,
"no-underscore-dangle": [2, {"allow": ["__data"]}],
"global-require": 0,
"no-restricted-syntax": 0,
"linebreak-style": 0,
"react/jsx-filename-extension": 0,
"import/imports-first": 0,
"no-class-assign": 0
"comma-dangle": "off", // Not sure why airbnb turned this on. Gross!
"indent": ["error", 2, {"SwitchCase": 1}],
"react/prefer-stateless-function": "off",
"react/prop-types": "off",
"react/jsx-closing-bracket-location": "off",
"no-console": "off",
"prefer-template": "off",
"max-len": "off",
"no-underscore-dangle": ["error", {"allow": ["__data"]}],
"global-require": "off",
"no-restricted-syntax": "off",
"linebreak-style": "off",
"react/jsx-filename-extension": "off",
"arrow-parens": ["error", "always"],
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
"jsx-a11y/anchor-is-valid": ["error", {
"specialLink": ["to"] // Allow "to" property in react-router-dom Link element
}],
"no-undef": "off", // TypeScript lint parser produces false positves; the compiler will error anyway
"no-unused-vars": "off", // TypeScript lint parser produces false positves; the compiler is set to error instead
"import/extensions": ["error", "ignorePackages", {
"js": "never",
"jsx": "never",
"ts": "never",
"tsx": "never"
}],
"react/sort-comp": ["error", { // Add type-annotations to the top
"order": [
"static-methods",
"type-annotations",
"instance-variables",
"lifecycle",
"/^on.+$/",
"getters",
"setters",
"/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/",
"instance-methods",
"everything-else",
"rendering"
]
}],
"typescript/adjacent-overload-signatures": ["error"],
"typescript/class-name-casing": ["error"],
"typescript/explicit-member-accessibility": ["error"],
"typescript/interface-name-prefix": ["error", "never"],
"typescript/member-delimiter-style": ["error"],
"typescript/no-angle-bracket-type-assertion": ["error"],
"typescript/no-array-constructor": ["error"],
"typescript/no-empty-interface": ["error"],
"typescript/no-inferrable-types": ["error"],
"typescript/no-unused-vars": ["error"],
"typescript/no-use-before-define": ["error"],
"typescript/type-annotation-spacing": ["error"]
},
"settings": {
"import/parser": "babel-eslint",
"import/resolver": {
"import/parser": "typescript-eslint-parser",
"import/resolver": {
"node": {
"moduleDirectory": ["node_modules", "Scripts"]
"moduleDirectory": ["node_modules", "Scripts"],
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
},
"import/extensions": [".js", ".jsx", ".ts", ".tsx"],
"no-underscore-dangle": {
"allow": ["__data"]
}
Expand Down
29 changes: 29 additions & 0 deletions src/ReactBoilerplate/.stylelintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"extends": [
"stylelint-config-standard"
],
"rules": {
'selector-pseudo-class-no-unknown': [ true, {
ignorePseudoClasses: [
'export',
'import',
'global',
'local'
],
}],
'property-no-unknown': [ true, {
ignoreProperties: [
'composes',
'compose-with'
],
}],
'at-rule-no-unknown': [ true, {
ignoreAtRules: [
'each',
'extend',
'include',
'mixin',
],
}]
}
}
21 changes: 0 additions & 21 deletions src/ReactBoilerplate/Scripts/client.js

This file was deleted.

36 changes: 36 additions & 0 deletions src/ReactBoilerplate/Scripts/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import { ConnectedRouter } from 'react-router-redux';
import { AppContainer } from 'react-hot-loader';
import configureStore from './redux/configureStore';
import ApiClient from './helpers/ApiClient';

// Need to import App directly from its file so that this module is dependent on it directly for HMR
import App from './containers/App/App';

const client = new ApiClient();
const history = createHistory();
const store = configureStore((window as any).__data, history, client);

const render = () => {
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<AppContainer>
<App />
</AppContainer>
</ConnectedRouter>
</Provider>,
document.getElementById('content')
);
};

// Render the App inside the store and HMR AppContainer
render();

// Register to accept changes to the App
if (module.hot) {
module.hot.accept('./containers/App/App', () => { render(); });
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React from 'react';
import Form from 'components/Form';
import * as React from 'react';
import { reduxForm } from 'redux-form';
import { Input } from 'components';
import { changeEmail } from 'redux/modules/manage';
import { RootState } from '../../redux/reducer';
import Form from '../Form';
import { Input } from '../../components';
import { changeEmail } from '../../redux/modules/manage/email';

class ChangeEmailForm extends Form {
constructor(props) {
class ChangeEmailForm extends Form<{}, { success: boolean }> {
public constructor(props: any) {
super(props);
this.success = this.success.bind(this);
this.state = { success: false };
}
success() {
public success() {
this.setState({ success: true });
}
render() {
public render() {
const {
fields: { currentPassword, email, emailConfirm }
} = this.props;
Expand All @@ -27,7 +28,7 @@ class ChangeEmailForm extends Form {
An email has been sent to your email to confirm the change.
</p>
}
{!success &&
{!success && currentPassword && email && emailConfirm &&
<form
onSubmit={this.handleApiSubmit(changeEmail, this.success)}
className="form-horizontal">
Expand All @@ -47,12 +48,11 @@ class ChangeEmailForm extends Form {
}
}

ChangeEmailForm = reduxForm({
form: 'changeEmail',
fields: ['currentPassword', 'email', 'emailConfirm']
},
(state) => state,
{ }
export default reduxForm(
{
form: 'changeEmail',
fields: ['currentPassword', 'email', 'emailConfirm']
},
(state: RootState) => state,
{ }
)(ChangeEmailForm);

export default ChangeEmailForm;
Loading