├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── client.test ├── .eslintrc └── test.js ├── client ├── components │ ├── Button │ │ ├── Button.js │ │ └── package.json │ ├── Layout │ │ ├── Header.css │ │ ├── Header.js │ │ ├── Layout.css │ │ ├── Layout.js │ │ ├── Logo.js │ │ ├── Navigation.js │ │ └── package.json │ └── Link │ │ ├── Link.js │ │ └── package.json ├── history.js ├── main.js ├── router.js ├── routes.json ├── store.js ├── utils │ ├── markdown-loader.js │ └── routes-loader.js └── views │ ├── about │ ├── About.js │ └── package.json │ ├── error │ ├── ErrorPage.css │ ├── ErrorPage.js │ └── package.json │ └── home │ ├── Home.js │ └── package.json ├── docs ├── README.md ├── recipes │ ├── how-to-integrate-material-design-lite.md │ └── how-to-use-sass.md └── routing-and-navigation.md ├── jsconfig.json ├── package.json ├── public ├── apple-touch-icon.png ├── browserconfig.xml ├── crossdomain.xml ├── favicon.ico ├── humans.txt ├── robots.txt ├── tile-wide.png └── tile.png ├── run.js ├── server.test ├── SampleTest.cs └── server.test.csproj ├── server ├── Controllers │ ├── AccountController.cs │ └── HomeController.cs ├── Models │ ├── DatabaseContext.cs │ └── User.cs ├── Startup.cs ├── Views │ └── Home │ │ └── Index.cshtml ├── appsettings.json ├── server.csproj └── web.config ├── starter-kit.sln ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.cs] 20 | indent_size = 4 21 | 22 | [*.md] 23 | trim_trailing_whitespace = false 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto 4 | 5 | # For the following file types, normalize line endings to LF on 6 | # checkin and prevent conversion to CRLF when they are checked out 7 | # (this is required in order to prevent newline related issues like, 8 | # for example, after the build script is run) 9 | .* text eol=lf 10 | *.cs text eol=lf 11 | *.cshtml text eol=lf 12 | *.css text eol=lf 13 | *.html text eol=lf 14 | *.js text eol=lf 15 | *.json text eol=lf 16 | *.md text eol=lf 17 | *.txt text eol=lf 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | 4 | # Build output 5 | build 6 | bin/ 7 | obj/ 8 | public/dist 9 | 10 | # Node.js and NPM 11 | node_modules 12 | npm-debug.log 13 | 14 | # .NET, Visual Studio 15 | .vs/ 16 | project.lock.json 17 | *.[Cc]ache 18 | !*.[Cc]ache/ 19 | 20 | # SQL Server 21 | *.mdf 22 | *.ldf 23 | 24 | # Sensitive data 25 | appsettings.*.json 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: node_js 4 | node_js: 5 | - '6' 6 | before_install: 7 | - sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list' 8 | - sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893 9 | - sudo apt-get update 10 | - sudo apt-get install dotnet-dev-1.0.1 11 | script: 12 | - npm run lint 13 | - npm run test 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Core Launch (console)", 6 | "type": "coreclr", 7 | "request": "launch", 8 | "preLaunchTask": "build", 9 | "program": "${workspaceRoot}/server/bin/Debug/netcoreapp1.0/server.dll", 10 | "args": [], 11 | "cwd": "${workspaceRoot}/server", 12 | "stopAtEntry": false 13 | }, 14 | { 15 | "name": ".NET Core Launch (web)", 16 | "type": "coreclr", 17 | "request": "launch", 18 | "preLaunchTask": "build", 19 | "program": "${workspaceRoot}/server/bin/Debug/netcoreapp1.0/server.dll", 20 | "args": [], 21 | "cwd": "${workspaceRoot}/server", 22 | "stopAtEntry": false, 23 | "launchBrowser": { 24 | "enabled": true, 25 | "args": "${auto-detect-url}", 26 | "windows": { 27 | "command": "cmd.exe", 28 | "args": "/C start ${auto-detect-url}" 29 | }, 30 | "osx": { 31 | "command": "open" 32 | }, 33 | "linux": { 34 | "command": "xdg-open" 35 | } 36 | } 37 | }, 38 | { 39 | "name": ".NET Core Attach", 40 | "type": "coreclr", 41 | "request": "attach", 42 | "processName": "" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // The default end of line character. 3 | "files.eol": "\n" 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "0.1.0", 5 | "command": "dotnet", 6 | "isShellCommand": true, 7 | "args": [], 8 | "tasks": [ 9 | { 10 | "taskName": "build", 11 | "args": ["server"], 12 | "isBuildCommand": true, 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "taskName": "test", 17 | "args": ["server.test"], 18 | "isTestCommand": true, 19 | "problemMatcher": "$msCompile" 20 | }, 21 | { 22 | "taskName": "publish", 23 | "args": ["-o", "build", "-f", "netcoreapp1.0", "server"], 24 | "isBuildCommand": true, 25 | "problemMatcher": "$msCompile" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ASP.NET Core Starter Kit 2 | 3 | ♥ ASP.NET Core Starter Kit and want to get involved? Thanks! There are plenty of ways you can help! 4 | 5 | Please take a moment to review this document in order to make the contribution process easy and 6 | effective for everyone involved. 7 | 8 | Following these guidelines helps to communicate that you respect the time of the developers managing 9 | and developing this open source project. In return, they should reciprocate that respect in 10 | addressing your issue or assessing patches and features. 11 | 12 | 13 | ## Using the issue tracker 14 | 15 | The [issue tracker](https://github.com/kriasoft/aspnet-starter-kit/issues) is the preferred channel 16 | for [bug reports](#bugs), [features requests](#features) and 17 | [submitting pull requests](#pull-requests), but please respect the following restrictions: 18 | 19 | * Please **do not** use the issue tracker for personal support requests (use 20 | [Stack Overflow](https://stackoverflow.com/questions/tagged/aspnet-starter-kit) or 21 | [Codementor](https://www.codementor.io/koistya)). 22 | 23 | * Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of 24 | others. 25 | 26 | 27 | 28 | ## Bug reports 29 | 30 | A bug is a _demonstrable problem_ that is caused by the code in the repository. Good bug reports are 31 | extremely helpful - thank you! 32 | 33 | Guidelines for bug reports: 34 | 35 | 1. **Use the GitHub issue search** — check if the issue has already been reported. 36 | 37 | 2. **Check if the issue has been fixed** — try to reproduce it using the latest `master` or 38 | development branch in the repository. 39 | 40 | 3. **Isolate the problem** — ideally create a [reduced test case](https://css-tricks.com/reduced-test-cases/) 41 | and a live example. 42 | 43 | A good bug report shouldn't leave others needing to chase you up for more information. Please try to 44 | be as detailed as possible in your report. What is your environment? What steps will reproduce the 45 | issue? What browser(s) and OS experience the problem? What would you expect to be the outcome? All 46 | these details will help people to fix any potential bugs. 47 | 48 | Example: 49 | 50 | > Short and descriptive example bug report title 51 | > 52 | > A summary of the issue and the browser/OS environment in which it occurs. If suitable, include the 53 | > steps required to reproduce the bug. 54 | > 55 | > 1. This is the first step 56 | > 2. This is the second step 57 | > 3. Further steps, etc. 58 | > 59 | > `` - a link to the reduced test case 60 | > 61 | > Any other information you want to share that is relevant to the issue being reported. This might 62 | > include the lines of code that you have identified as causing the bug, and potential solutions 63 | > (and your opinions on their merits). 64 | 65 | 66 | 67 | ## Feature requests 68 | 69 | Feature requests are welcome. But take a moment to find out whether your idea fits with the scope 70 | and aims of the project. It's up to *you* to make a strong case to convince the project's developers 71 | of the merits of this feature. Please provide as much detail and context as possible. 72 | 73 | 74 | 75 | ## Pull requests 76 | 77 | Good pull requests - patches, improvements, new features - are a fantastic help. They should remain 78 | focused in scope and avoid containing unrelated commits. 79 | 80 | **Please ask first** before embarking on any significant pull request (e.g. implementing features, 81 | refactoring code, porting to a different language), otherwise you risk spending a lot of time 82 | working on something that the project's developers might not want to merge into the project. 83 | 84 | Please adhere to the coding conventions used throughout a project (indentation, accurate comments, 85 | etc.) and any other requirements (such as test coverage). 86 | 87 | Adhering to the following process is the best way to get your work included in the project: 88 | 89 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) the project, clone your fork, and configure 90 | the remotes: 91 | 92 | ```bash 93 | # Clone your fork of the repo into the current directory 94 | git clone https://github.com//aspnet-starter-kit.git 95 | # Navigate to the newly cloned directory 96 | cd aspnet-starter-kit 97 | # Assign the original repo to a remote called "upstream" 98 | git remote add upstream https://github.com/kriasoft/aspnet-starter-kit.git 99 | ``` 100 | 101 | 2. If you cloned a while ago, get the latest changes from upstream: 102 | 103 | ```bash 104 | git checkout master 105 | git pull upstream master 106 | ``` 107 | 108 | 3. Create a new topic branch (off the main project development branch) to contain your feature, 109 | change, or fix: 110 | 111 | ```bash 112 | git checkout -b 113 | ``` 114 | 115 | 4. Commit your changes in logical chunks. Please adhere to these [git commit message 116 | guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is 117 | unlikely be merged into the main project. Use Git's [interactive 118 | rebase](https://help.github.com/articles/about-git-rebase/) feature to tidy up your commits 119 | before making them public. 120 | 121 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 122 | 123 | ```bash 124 | git pull [--rebase] upstream master 125 | ``` 126 | 127 | 6. Push your topic branch up to your fork: 128 | 129 | ```bash 130 | git push origin 131 | ``` 132 | 133 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title 134 | and description. 135 | 136 | **IMPORTANT**: By submitting a patch, you agree to allow the project owners to license your work 137 | under the terms of the [MIT License](LICENSE.txt). 138 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014-2016 Kriasoft (https://kriasoft.com). All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Starter Kit   2 | 3 | > [ASP.NET Core Starter Kit](https://github.com/kriasoft/aspnet-starter-kit) is a real-world 4 | > boilerplate and tooling for creating [single-page web applications](https://en.wikipedia.org/wiki/Single-page_application) 5 | > (SPA) oriented towards [progressive enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement) 6 | > design, cross-platform compatability and component-based UI architecture. It is built upon best of 7 | > breed technologies including [.NET Core](https://dot.net/core), [Kestrel](https://github.com/aspnet/KestrelHttpServer), 8 | > [EF Core](https://ef.readthedocs.io/en/latest/), [Babel](http://babeljs.io/), [Webpack](https://webpack.github.io/), 9 | > [React](https://facebook.github.io/react), [Redux](http://redux.js.org/), [CSS Modules](https://github.com/css-modules/css-modules), 10 | > [React Hot Loader](http://gaearon.github.io/react-hot-loader/) and more. This boilerplate comes in 11 | > both [C#](https://github.com/kriasoft/aspnet-starter-kit) and [F#](https://github.com/kriasoft/fsharp-starter-kit) flavors. 12 | 13 | **See** [demo](https://aspnet-core.azurewebsites.net), [docs](docs)  |  **Follow us** on 14 | [Gitter](https://gitter.im/kriasoft/aspnet-starter-kit) or [Twitter](https://twitter.com/dotnetreact) 15 |  |  **Learn** [React.js, ES6 and ASP.NET Core](#learn-reactjs-es6-and-aspnet-core) 16 |  |  **Visit our sponsors**: 17 | 18 |

19 | 20 | 21 | Hiring 22 |

23 | 24 | 25 | ### Features 26 | 27 |     ✓ Component-based front-end development via [Webpack](https://webpack.github.io/), [CSS Modules](https://github.com/css-modules/css-modules) and [React](https://facebook.github.io/react) (see [`webpack.config.js`](webpack.config.js))
28 |     ✓ Modern JavaScript syntax ([ES2015](http://babeljs.io/docs/learn-es2015/)+) via [Babel](http://babeljs.io/); modern CSS syntax (CSS3+) via [PostCSS](https://github.com/postcss/postcss)
29 |     ✓ Application state management via [Redux](http://redux.js.org/) (see [`client/store.js`](client/store.js))
30 |     ✓ Universal cross-stack routing and navigation via [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) and [`history`](https://github.com/ReactJSTraining/history) (see [`client/routes.json`](client/routes.json))
31 |     ✓ [Code-splitting](https://github.com/webpack/docs/wiki/code-splitting) and async chunk loading with [Webpack](https://webpack.github.io/) and [ES6 System.import()](http://www.2ality.com/2014/09/es6-modules-final.html)
32 |     ✓ Hot Module Replacement ([HMR](https://webpack.github.io/docs/hot-module-replacement.html)) /w [React Hot Loader](http://gaearon.github.io/react-hot-loader/)
33 |     ✓ Lightweight build automation with plain JavaScript (see [`run.js`](run.js))
34 |     ✓ Cross-device testing with [Browsersync](https://browsersync.io/)
35 |     ✓ Git-based deployment to [Azure App Service](https://azure.microsoft.com/services/app-service/) (see [`run.js/publish`](run.js))
36 |     ✓ **24/7** community support on [Gitter](https://gitter.im/kriasoft/aspnet-starter-kit) or [StackOverflow](http://stackoverflow.com/questions/tagged/aspnet-starter-kit); consulting and customization requests on [Codementor](https://www.codementor.io/koistya)
37 | 38 | 39 | ### Directory Layout 40 | 41 | ```shell 42 | . 43 | ├── /.vscode/ # Visual Studio Code settings 44 | ├── /build/ # The folder for compiled output 45 | ├── /client/ # Client-side app (frontend) 46 | │ ├── /components/ # Common or shared UI components 47 | │ ├── /utils/ # Helper functions and utility classes 48 | │ ├── /views/ # UI components for web pages (screens) 49 | │ ├── history.js # HTML5 History API wrapper used for navigation 50 | │ ├── main.js # Entry point that bootstraps the app 51 | │ ├── router.js # Lightweight application router 52 | │ ├── routes.json # The list of application routes 53 | │ └── store.js # Application state manager (Redux) 54 | ├── /client.test/ # Unit and integration tests for the frontend app 55 | ├── /docs/ # Documentation to the project 56 | ├── /public/ # Static files such as favicon.ico etc. 57 | │ ├── robots.txt # Instructions for search engine crawlers 58 | │ └── ... # etc. 59 | ├── /server/ # Web server and data API (backend) 60 | │ ├── /Controllers/ # ASP.NET Web API and MVC controllers 61 | │ ├── /Models/ # Entity Framework models (entities) 62 | │ ├── /Views/ # Server-side rendered views 63 | │ ├── appsettings.json # Server-side application settings 64 | │ ├── Startup.cs # Server-side application entry point 65 | │ └── web.config # Web server settings for IIS 66 | ├── /server.test/ # Unit and integration tests for the backend app 67 | │── jsconfig.json # Visual Studio Code settings for JavaScript 68 | │── package.json # The list of project dependencies and NPM scripts 69 | │── run.js # Build automation script (similar to gulpfile.js) 70 | └── webpack.config.js # Bundling and optimization settings for Webpack 71 | ``` 72 | 73 | 74 | ### Prerequisites 75 | 76 | * OS X, Windows or Linux 77 | * [Node.js](https://nodejs.org) v6 or newer 78 | * [.NET Core](https://www.microsoft.com/net/core) and [.NET Core SDK](https://www.microsoft.com/net/core) 79 | * [Visual Studio Code](https://code.visualstudio.com/) with [C# extension](https://github.com/OmniSharp/omnisharp-vscode) (or Visual Studio 2015 or newer) 80 | 81 | 82 | ### Getting Started 83 | 84 | **Step 1**. Clone the latest version of **ASP.NET Core Starter Kit** on your local machine by running: 85 | 86 | ```shell 87 | $ git clone -o aspnet-starter-kit -b master --single-branch \ 88 | https://github.com/kriasoft/aspnet-starter-kit.git MyApp 89 | $ cd MyApp 90 | ``` 91 | 92 | Alternatively, scaffold your project with [Yeoman](http://yeoman.io/): 93 | 94 | ```shell 95 | $ npm install -g yo 96 | $ npm install -g generator-aspnetcore 97 | $ yo aspnetcore 98 | ``` 99 | 100 | **Step 2**. Install project dependencies listed in [`project.json`](server/project.json) and 101 | [`package.json`](package.json) files: 102 | 103 | ```shell 104 | $ npm install # Install both Node.js and .NET Core dependencies 105 | ``` 106 | 107 | **Step 3**. Finally, launch your web app: 108 | 109 | ```shell 110 | $ node run # Compile and lanch the app, same as running: npm start 111 | ``` 112 | 113 | The app should become available at [http://localhost:5000/](http://localhost:5000/). 114 | See [`run.js`](run.js) for other available commands such as `node run build`, `node run publish` etc. 115 | You can also run your app in a release (production) mode by running `node run --release`, or without 116 | Hot Module Replacement (HMR) by running `node run --no-hmr`. 117 | 118 | 119 | ### How to Deploy 120 | 121 | Before you can deploy your app to [Azure App Service](https://azure.microsoft.com/services/app-service/), 122 | you need to open Web App settings in [Azure Portal](https://portal.azure.com/), go to "Deployment 123 | Source", select "Local Git Repository" and hit [OK]. Then copy and paste "Git clone URL" of your 124 | Web App into [`run.js/publish`](run.js) file. Finally, whenever you need to compile your 125 | app into a distributable format and upload that to Windows Azure App Service, simply run: 126 | 127 | ```shell 128 | $ node run publish # Same as running: npm run publish 129 | ``` 130 | 131 | ### How to Update 132 | 133 | We work hard on keeping the project up-to-date and adding new features. Down the road, after 134 | starting a new web application project based on this boilerplate, you can always fetch and merge 135 | the latest changes from this (upstream) repo back into your project by running: 136 | 137 | ```shell 138 | $ git checkout master 139 | $ git fetch aspnet-starter-kit 140 | $ git merge aspnet-starter-kit/master 141 | ``` 142 | 143 | Alternatively, pull the latest version of this repository into a separate folder and compare it with 144 | your project by using a diff tool such as [Beyond Compare](http://www.scootersoftware.com/). 145 | 146 | 147 | ### How to Contribute 148 | 149 | Anyone and everyone is welcome to [contribute](CONTRIBUTING.md) to this project. The best way to 150 | start is by checking our [open issues](https://github.com/kriasoft/aspnet-starter-kit/issues), 151 | [submit a new issues](https://github.com/kriasoft/aspnet-starter-kit/issues/new?labels=bug) or 152 | [feature request](https://github.com/kriasoft/aspnet-starter-kit/issues/new?labels=enhancement), 153 | participate in discussions, upvote or downvote the issues you like or dislike, send [pull 154 | requests](CONTRIBUTING.md#pull-requests). 155 | 156 | 157 | ### Learn React.js, ES6 and ASP.NET Core 158 | 159 | :mortar_board:   **[React.js Training Program](http://www.reactjsprogram.com/?asdf=36750_q0pu0tfa)** by Tyler McGinnis
160 | :mortar_board:   **[React for Beginners](https://reactforbeginners.com/friend/konstantin)** and **[ES6 Training Course](https://es6.io/friend/konstantin)** by Wes Bos
161 | :green_book:   **[React: Up & Running: Building Web Applications](http://amzn.to/2bBkZs1)** by Stoyan Stefanov (Aug, 2016)
162 | :green_book:   **[Getting Started with React](http://amzn.to/2beVRI9)** by Doel Sengupta and Manu Singhal (Apr, 2016)
163 | :green_book:   **[You Don't Know JS: ES6 & Beyond](http://amzn.to/2bFzlqe)** by Kyle Simpson (Dec, 2015)
164 | :green_book:   **[C# 6 and .NET Core 1.0: Modern Cross-Platform Development](http://amzn.to/2beV5uS)** by Mark J. Price (Mar, 2016)
165 | :green_book:   **[Professional C# 6 and .NET Core 1.0](http://amzn.to/2bhILsn)** by Christian Nagel (Apr, 2016)
166 | 167 | 168 | ### Related Projects 169 | 170 | * [React App SDK](https://github.com/kriasoft/react-app) — build React applications with a single dev dependency and no build configuration 171 | * [React Starter Kit](https://github.com/kriasoft/react-starter-kit) — Isomorphic web app boilerplate (Node.js, Express, GraphQL, React) 172 | * [Babel Starter Kit](https://github.com/kriasoft/babel-starter-kit) — JavaScript library boilerplate (ES2015+, Babel, Rollup) 173 | * [ASP.NET Core Starter Kit `|>` F#](https://github.com/kriasoft/fsharp-starter-kit) — Web app boilerplate (F#, .NET Core, Kestrel, GraphQL, React) 174 | * [Universal Router](https://github.com/kriasoft/universal-router) — Isomorphic router for web and single-page applications (SPA) 175 | * [Membership Database](https://github.com/membership/membership.db) — SQL database boilerplate for web app users, roles and auth tokens 176 | 177 | 178 | ### Get in Touch 179 | 180 | * [#aspnet-starter-kit](https://gitter.im/kriasoft/aspnet-starter-kit) on Gitter 181 | * [@koistya](https://twitter.com/koistya) on [Codementor](https://www.codementor.io/koistya) 182 | or [Skype](http://hatscripts.com/addskype?koistya) 183 | 184 | 185 | ### License 186 | 187 | Copyright © 2014-present [Kriasoft](https://kriasoft.com). This source code is licensed under the MIT 188 | license found in the [LICENSE.txt](https://github.com/kriasoft/react-starter-kit/blob/master/LICENSE.txt) 189 | file. The documentation to the project is licensed under the [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) 190 | license. 191 | 192 | 193 | --- 194 | Made with ♥ by Konstantin Tarkus ([@koistya](https://twitter.com/koistya)) and [contributors](https://github.com/kriasoft/aspnet-starter-kit/graphs/contributors) 195 | -------------------------------------------------------------------------------- /client.test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "rules": { 6 | "no-console": 0, 7 | "no-unused-expressions": 0, 8 | "padded-blocks": 0 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client.test/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ASP.NET Core Starter Kit (https://dotnetreact.com) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { expect } from 'chai'; 11 | 12 | describe('test', () => { 13 | 14 | it('should pass', () => { 15 | expect(1).to.be.ok; 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /client/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ASP.NET Core Starter Kit (https://dotnetreact.com) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React, { PropTypes } from 'react'; 11 | import cx from 'classnames'; 12 | 13 | class Button extends React.Component { 14 | 15 | static propTypes = { 16 | className: PropTypes.string, 17 | }; 18 | 19 | componentDidMount() { 20 | window.componentHandler.upgradeElement(this.root); 21 | } 22 | 23 | componentWillUnmount() { 24 | window.componentHandler.downgradeElements(this.root); 25 | } 26 | 27 | render() { 28 | const { className, ...other } = this.props; 29 | return ( 30 | 122 | 123 | ); 124 | } 125 | 126 | export default MyComponent; 127 | ``` 128 | 129 | ### Step 4 130 | 131 | Extend MDL components with your own styles (via [CSS Modules](https://github.com/css-modules/css-modules) 132 | or [inline styles](https://facebook.github.io/react/tips/inline-styles.html)): 133 | 134 | #### `components/Spinner/Spinner.css` 135 | 136 | ```css 137 | .spinner { 138 | border: 1px solid red; 139 | } 140 | ``` 141 | 142 | #### `components/Spinner/Spinner.js` 143 | 144 | ```js 145 | import React, { PropTypes } from 'react'; 146 | import s from './Spinner.css'; 147 | 148 | class Spinner extends React.Component { 149 | 150 | static propTypes = { 151 | isActive: PropTypes.bool, 152 | }; 153 | 154 | componentDidMount() { 155 | window.componentHandler.upgradeElement(this.root); 156 | } 157 | 158 | componentWillUnmount() { 159 | window.componentHandler.downgradeElements(this.root); 160 | } 161 | 162 | render() { 163 | const active = this.props.isActive ? ' is-active' : ''; 164 | return ( 165 |
(this.root = node)} 167 | className={`mdl-spinner mdl-js-spinner ${s.spinner}${active}`} 168 | /> 169 | ); 170 | } 171 | 172 | } 173 | 174 | export default Spinner; 175 | ``` 176 | -------------------------------------------------------------------------------- /docs/recipes/how-to-use-sass.md: -------------------------------------------------------------------------------- 1 | ## How to Use Sass/SCSS 2 | 3 | > **Note**: Using plain CSS via [PostCSS](http://postcss.org/) is recommended approach because it 4 | reduces the size of the tech stack used in the project, enforces you to learn vanilla CSS syntax 5 | with modern CSS Level 3+ features that allow you doing everything you would normally do with 6 | Sass/SCSS. Also compilation of plain `.css` files should work faster with `postcss` pre-processor 7 | than `node-sass`. 8 | 9 | ### Step 1 10 | 11 | Install [`node-sass`](https://github.com/sass/node-sass) and 12 | [`sass-loader`](https://github.com/jtangelder/sass-loader) modules as dev dependencies: 13 | 14 | ```sh 15 | $ npm install node-sass --save-dev 16 | $ npm install sass-loader --save-dev 17 | ``` 18 | 19 | ### Step 2 20 | 21 | Update [`webpack.config.js`](../../webpack.config.js) file to use `sass-loader` for `.scss` files: 22 | 23 | ```js 24 | const config = { 25 | ... 26 | module: { 27 | loaders: [ 28 | ... 29 | { 30 | test: /\.scss$/, 31 | loaders: [ 32 | 'style-loader', 33 | `css-loader?${JSON.stringify({ sourceMap: isDebug, minimize: !isDebug })}`, 34 | 'postcss-loader?pack=sass', 35 | 'sass-loader', 36 | ], 37 | }, 38 | ... 39 | ] 40 | } 41 | ... 42 | } 43 | ``` 44 | 45 | ### Step 3 46 | 47 | Add one more configuration (pack) for [PostCSS](https://github.com/postcss/postcss) named `sass` to 48 | enable [Autoprefixer](https://github.com/postcss/autoprefixer) for your `.scss` files: 49 | 50 | ```js 51 | const config = { 52 | ... 53 | postcss(bundler) { 54 | return { 55 | defaults: [ 56 | ... 57 | ], 58 | sass: [ 59 | require('autoprefixer')(), 60 | ], 61 | }; 62 | } 63 | ... 64 | } 65 | ``` 66 | 67 | For more information visit https://github.com/jtangelder/sass-loader and https://github.com/sass/node-sass 68 | -------------------------------------------------------------------------------- /docs/routing-and-navigation.md: -------------------------------------------------------------------------------- 1 | ## Routing and Navigation 2 | 3 | [ASP.NET Core Starter Kit](https://github.com/kriasoft/aspnet-starter-kit) uses a custom 4 | minimalistic (under 100 LOC) declarative routing approach that is easy to customize. It's comprised 5 | of five major parts: 6 | 7 | * **Routes** — the list of application routes in JSON format (see [`client/routes.json`](../client/routes.json)) 8 | * **Routes Loader** — a custom loader for Webpack that converts routes from JSON to JavaScript on 9 | build (see [`client/utils/routes-loader.js`](../client/utils/routes-loader.js)) 10 | * **URL Matcher** — a function that checks if a given URI matches to the route's `path` string (see 11 | `matchURI()` method in [`client/router.js`](../client/router.js)) 12 | * **Route Resolver** — a function just resolves a URI string to the first matched route, fetches 13 | all the required data and returns a React component to render (see `resolve()` method in 14 | [`client/router.js`](../client/router.js)) 15 | * **History** — client-side navigation library powered by [`history`](https://github.com/ReactJSTraining/history) 16 | npm module (the same one used in `react-router`) that helps with transitioning between pages 17 | (screens) in the browser without causing full-page refresh (see [`client/history.js`](../client/history.js)) 18 | 19 | The list of routes is just an array where each item contains a `path` - parametrized URL path string 20 | and a `view` field that points to a corresponding UI (page or screen) component within the project's 21 | file structure. For a simple to-do app, this list of routes may look like this (`routes.json`): 22 | 23 | ```json 24 | [ 25 | { 26 | "path": "/", 27 | "view": "./views/home" 28 | }, 29 | { 30 | "path": "/tasks/:status(pending|completed)?", 31 | "view": "./views/tasks/list" 32 | }, 33 | { 34 | "path": "/tasks/new", 35 | "view": "./views/tasks/new" 36 | }, 37 | { 38 | "path": "/tasks/:id", 39 | "view": "./views/tasks/details" 40 | } 41 | ] 42 | ``` 43 | 44 | This list of routes is referenced inside the main application file (where the React app is beeing 45 | bootstrapped) by using [`routes-loader`](../client/utils/routes-loader.js) (see 46 | [`client/main.js`](../client/main.js)): 47 | 48 | ```js 49 | import routes from '!!./utils/routes-loader!./routes.json'; 50 | ``` 51 | 52 | If you're new to Webpack's "loader" concept, please refer to https://webpack.github.io/docs/loaders 53 | 54 | The [`routes-loader`](../client/utils/routes-loader.js) performs three tasks: 55 | 56 | * Converts JSON-based routes into JavaScript 57 | * Converts parametrized URL path strings into regular expressions by using 58 | [`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) 59 | * Wraps page/screen UI components' path strings into either `require.ensure(..)` (Webpack 1.x) or 60 | `System.import(..)` (Webpack 2.x). For more information see 61 | [code-splitting](https://webpack.github.io/docs/code-splitting) in Webpack docs. 62 | 63 | For example, a route like this: 64 | 65 | ```json 66 | { 67 | "path": "/tasks/:id", 68 | "view": "./views/tasks/details" 69 | } 70 | ``` 71 | 72 | Will become: 73 | 74 | ```js 75 | { 76 | path: '/tasks/:id', 77 | pattern: /^\/tasks\/((?:[^\/]+?))(?:\/(?=$))?$/i, 78 | keys: [{ name: 'id', pattern: '[^\\/]+?', ... }], 79 | view: './views/tasks/details', 80 | load: function() { return System.import('./views/tasks/details'); } 81 | } 82 | ``` 83 | 84 | Given the list of routes you can ask the router to "resolve" the given URI string to a React 85 | component. The code for that may look something like this: 86 | 87 | ```js 88 | router.resolve(routes, { pathname: '/tasks/123' }).then(component => { 89 | ReactDOM.render(component, container); 90 | }); 91 | ``` 92 | 93 | The `resolve(routes, context)` method will find the first route from the list matching to the 94 | `/tasks/123` URI string, execute its `load()` method, and return corresponding React component as a 95 | result wrapped into ES6 Promise (see [`core/router.js`](../core/router.js). 96 | 97 | If a route contains some REST API or GraphQL endpoints as data requirements for the given route, 98 | the `resolve(..)` method can also fetch the required data from these endpoints. For example, a 99 | route that needs to fetch a task by its ID may look like this: 100 | 101 | ```json 102 | { 103 | "path": "/tasks/:id", 104 | "view": "./views/tasks/details", 105 | "fetch": { 106 | "task": "GET /api/tasks/$id", 107 | } 108 | } 109 | ``` 110 | 111 | Finally, you can hook the router's `resolve(..)` method to be called each time when a user navigates 112 | (transitions) between pages. The code for that may look something like this: 113 | 114 | ```js 115 | function render(location) { 116 | router.resolve(routes, location) 117 | .then(renderComponent) 118 | .catch(error => router.resolve(routes, { ...location, error }).then(renderComponent)); 119 | } 120 | 121 | history.listen(render); 122 | render(history.getCurrentLocation()); 123 | ``` 124 | 125 | For more information about how the `history` npm module works please visit: 126 | 127 | https://github.com/ReactJSTraining/history/tree/master/docs 128 | 129 | All transitions between pages must be performed by using this module, for example: 130 | 131 | ```js 132 | import React from 'react'; 133 | import history from '../../core/history'; 134 | 135 | class HomePage extends React.Component { 136 | 137 | transition = event => { 138 | event.preventDefault(); 139 | history.push({ pathname: event.currentTarget.pathname }); 140 | }; 141 | 142 | render() { 143 | return ( 144 |
145 |

Home Page

146 |

Show task #123

147 |
148 | ); 149 | } 150 | 151 | } 152 | ``` 153 | 154 | The `transition(event)` method above cancels default behavior of the `` element that causes 155 | full-page refresh and instead redirects a user to the `/tasks/123` page by using HTML5 History API. 156 | This transition is then handled by `history.listen(render)` listener inside the 157 | [`main.js`](../main.js) file. 158 | 159 | RSB comes with a helper component that can be used instead of `` elements, see 160 | [`client/components/Link/Link.js`](../client/components/Link/Link.js). So, instead of writing 161 | `Show task #123` you can have `Show task #123`. 163 | 164 | ### Related Articles 165 | 166 | * [You might not need React Router](https://medium.com/@tarkus/you-might-not-need-react-router-38673620f3d) by Konstantin Tarkus -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=759670 3 | // for the documentation about the jsconfig.json format 4 | "compilerOptions": { 5 | "target": "es6", 6 | "module": "commonjs", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "exclude": [ 10 | "build", 11 | "node_modules", 12 | "temp", 13 | "tmp" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "engines": { 6 | "node": ">=6", 7 | "npm": ">=3.8" 8 | }, 9 | "dependencies": { 10 | "babel-polyfill": "6.23.0", 11 | "classnames": "2.2.5", 12 | "fastclick": "1.0.6", 13 | "history": "4.6.1", 14 | "query-string":"^4.3.4", 15 | "react": "15.5.4", 16 | "react-dom": "15.5.4", 17 | "react-mdl": "1.10.3", 18 | "react-redux": "5.0.5", 19 | "redux": "3.6.0", 20 | "whatwg-fetch": "^2.0.3" 21 | }, 22 | "devDependencies": { 23 | "assets-webpack-plugin": "^3.5.1", 24 | "autoprefixer": "^7.1.1", 25 | "babel-core": "^6.24.1", 26 | "babel-eslint": "^7.2.3", 27 | "babel-loader": "^7.0.0", 28 | "babel-plugin-transform-class-properties": "6.24.1", 29 | "babel-plugin-transform-object-rest-spread": "6.23.0", 30 | "babel-plugin-transform-react-constant-elements": "6.23.0", 31 | "babel-plugin-transform-regenerator": "6.24.1", 32 | "babel-plugin-transform-runtime": "6.23.0", 33 | "babel-preset-latest": "^6.24.1", 34 | "babel-preset-react": "^6.24.1", 35 | "babel-register": "^6.24.1", 36 | "babel-runtime": "^6.23.0", 37 | "browser-sync": "^2.18.12", 38 | "chai": "^4.0.1", 39 | "cpy": "^5.0.0", 40 | "css-loader": "^0.28.4", 41 | "del": "^2.2.2", 42 | "eslint": "^3.19.0", 43 | "eslint-config-airbnb": "^15.0.1", 44 | "eslint-plugin-import": "^2.3.0", 45 | "eslint-plugin-jsx-a11y": "^5.0.3", 46 | "eslint-plugin-react": "^7.0.1", 47 | "extract-text-webpack-plugin": "^2.1.0", 48 | "file-loader": "^0.11.1", 49 | "front-matter": "^2.1.2", 50 | "highlight.js": "^9.12.0", 51 | "json-loader": "^0.5.4", 52 | "markdown-it": "^8.3.1", 53 | "mkdirp": "^0.5.1", 54 | "mocha": "^3.4.2", 55 | "path-to-regexp": "^1.7.0", 56 | "pixrem": "^3.0.2", 57 | "pleeease-filters": "^4.0.0", 58 | "postcss": "^6.0.1", 59 | "postcss-calc": "^6.0.0", 60 | "postcss-color-function": "^4.0.0", 61 | "postcss-custom-media": "^6.0.0", 62 | "postcss-custom-properties": "^6.0.1", 63 | "postcss-custom-selectors": "^4.0.1", 64 | "postcss-import": "^10.0.0", 65 | "postcss-loader": "^2.0.5", 66 | "postcss-media-minmax": "^3.0.0", 67 | "postcss-nesting": "^4.0.1", 68 | "postcss-selector-matches": "^3.0.1", 69 | "postcss-selector-not": "^3.0.1", 70 | "react-hot-loader": "^3.0.0-beta.2", 71 | "style-loader": "^0.18.1", 72 | "stylelint": "^7.10.1", 73 | "stylelint-config-standard": "^16.0.0", 74 | "url-loader": "^0.5.8", 75 | "webpack": "^2.6.1", 76 | "webpack-dev-middleware": "^1.10.2", 77 | "webpack-hot-middleware": "^2.18.0" 78 | }, 79 | "babel": { 80 | "presets": [ 81 | "latest", 82 | "react" 83 | ], 84 | "plugins": [ 85 | "react-hot-loader/babel", 86 | "transform-class-properties", 87 | "transform-object-rest-spread", 88 | [ 89 | "transform-regenerator", 90 | { 91 | "async": false 92 | } 93 | ], 94 | [ 95 | "transform-runtime", 96 | { 97 | "helpers": false, 98 | "polyfill": false, 99 | "regenerator": true 100 | } 101 | ] 102 | ] 103 | }, 104 | "eslintConfig": { 105 | "env": { 106 | "browser": true 107 | }, 108 | "parser": "babel-eslint", 109 | "extends": "airbnb", 110 | "rules": { 111 | "react/jsx-filename-extension": 0, 112 | "import/no-extraneous-dependencies": 0 113 | } 114 | }, 115 | "stylelint": { 116 | "extends": "stylelint-config-standard", 117 | "rules": { 118 | "string-quotes": "single" 119 | } 120 | }, 121 | "scripts": { 122 | "postinstall": "dotnet restore server && dotnet restore server.test", 123 | "eslint": "eslint client client.test run.js webpack.config.js", 124 | "stylelint": "stylelint \"client/components/**/*.css\" \"client/views/**/*.css\"", 125 | "lint": "npm run eslint && npm run stylelint", 126 | "test": "mocha \"client.test\" --compilers js:babel-register", 127 | "test:watch": "mocha \"client.test\" --compilers js:babel-register --reporter min --watch", 128 | "clean": "node run clean", 129 | "build": "node run build", 130 | "build:debug": "node run build --debug", 131 | "publish": "node run publish", 132 | "publish:debug": "node run publish --debug", 133 | "start": "node run" 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/aspnet-starter-kit/02679e9fa48268127c4223fc099ad2928a24a499/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/aspnet-starter-kit/02679e9fa48268127c4223fc099ad2928a24a499/public/favicon.ico -------------------------------------------------------------------------------- /public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | ASP.NET Core, GraphQL, React 16 | Webpack, Babel, Node.js 17 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /public/tile-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/aspnet-starter-kit/02679e9fa48268127c4223fc099ad2928a24a499/public/tile-wide.png -------------------------------------------------------------------------------- /public/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/aspnet-starter-kit/02679e9fa48268127c4223fc099ad2928a24a499/public/tile.png -------------------------------------------------------------------------------- /run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ASP.NET Core Starter Kit (https://dotnetreact.com) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable global-require, no-console, import/newline-after-import */ 11 | 12 | const fs = require('fs'); 13 | const del = require('del'); 14 | const cpy = require('cpy'); 15 | const path = require('path'); 16 | const mkdirp = require('mkdirp'); 17 | const webpack = require('webpack'); 18 | const cp = require('child_process'); 19 | 20 | const tasks = new Map(); 21 | 22 | function run(task) { 23 | const start = new Date(); 24 | console.log(`Starting '${task}'...`); 25 | return Promise.resolve().then(() => tasks.get(task)()).then(() => { 26 | console.log(`Finished '${task}' after ${new Date().getTime() - start.getTime()}ms`); 27 | }, err => console.error(err.stack)); 28 | } 29 | 30 | // 31 | // Clean up the output directory 32 | // ----------------------------------------------------------------------------- 33 | tasks.set('clean', () => Promise.resolve() 34 | .then(() => del(['build/*', 'public/dist/*', '!build/.git'], { dot: true })) 35 | .then(() => { 36 | mkdirp.sync('build/public/dist'); 37 | mkdirp.sync('public/dist'); 38 | }) 39 | ); 40 | 41 | // 42 | // Bundle JavaScript, CSS and image files with Webpack 43 | // ----------------------------------------------------------------------------- 44 | tasks.set('bundle', () => { 45 | const webpackConfig = require('./webpack.config'); 46 | return new Promise((resolve, reject) => { 47 | webpack(webpackConfig).run((err, stats) => { 48 | if (err) { 49 | reject(err); 50 | } else { 51 | console.log(stats.toString(webpackConfig.stats)); 52 | resolve(); 53 | } 54 | }); 55 | }); 56 | }); 57 | 58 | // 59 | // Copy static files into the output folder 60 | // ----------------------------------------------------------------------------- 61 | tasks.set('copy', () => cpy(['public/**/*.*'], 'build', { parents: true })); 62 | 63 | // 64 | // Copy ASP.NET application config file for production and development environments 65 | // ----------------------------------------------------------------------------- 66 | tasks.set('appsettings', () => new Promise(resolve => { 67 | const environments = ['Production', 'Development']; 68 | let count = environments.length; 69 | const source = require('./server/appsettings.json'); // eslint-disable-line global-require 70 | delete source.Logging; 71 | environments.forEach(env => { 72 | const filename = path.resolve(__dirname, `./server/appsettings.${env}.json`); 73 | try { 74 | fs.writeFileSync(filename, JSON.stringify(source, null, ' '), { flag: 'wx' }); 75 | } catch (err) {} // eslint-disable-line no-empty 76 | if (--count === 0) resolve(); 77 | }); 78 | })); 79 | 80 | 81 | // 82 | // Copy static files into the output folder 83 | // ----------------------------------------------------------------------------- 84 | tasks.set('build', () => { 85 | global.DEBUG = process.argv.includes('--debug') || false; 86 | return Promise.resolve() 87 | .then(() => run('clean')) 88 | .then(() => run('bundle')) 89 | .then(() => run('copy')) 90 | .then(() => run('appsettings')) 91 | .then(() => new Promise((resolve, reject) => { 92 | const options = { stdio: ['ignore', 'inherit', 'inherit'] }; 93 | const config = global.DEBUG ? 'Debug' : 'Release'; 94 | const args = ['publish', 'server', '-o', '../build', '-c', config]; 95 | cp.spawn('dotnet', args, options).on('close', code => { 96 | if (code === 0) { 97 | resolve(); 98 | } else { 99 | reject(new Error(`dotnet ${args.join(' ')} => ${code} (error)`)); 100 | } 101 | }); 102 | })); 103 | }); 104 | 105 | 106 | // 107 | // Build and publish web application to Azure Web Apps 108 | // ----------------------------------------------------------------------------- 109 | tasks.set('publish', () => { 110 | global.DEBUG = process.argv.includes('--debug') || false; 111 | const remote = { 112 | name: 'azure', 113 | url: 'https://@.scm.azurewebsites.net:443/.git', // TODO: Update deployment URL 114 | }; 115 | const opts = { cwd: path.resolve(__dirname, './build'), stdio: ['ignore', 'inherit', 'inherit'] }; 116 | const git = (...args) => new Promise((resolve, reject) => { 117 | cp.spawn('git', args, opts).on('close', code => { 118 | if (code === 0) { 119 | resolve(); 120 | } else { 121 | reject(new Error(`git ${args.join(' ')} => ${code} (error)`)); 122 | } 123 | }); 124 | }); 125 | 126 | return Promise.resolve() 127 | .then(() => run('clean')) 128 | .then(() => git('init', '--quiet')) 129 | .then(() => git('config', '--get', `remote.${remote.name}.url`) 130 | .then(() => git('remote', 'set-url', remote.name, remote.url)) 131 | .catch(() => git('remote', 'add', remote.name, remote.url)) 132 | ) 133 | .then(() => git('ls-remote', '--exit-code', remote.url, 'master') 134 | .then(() => Promise.resolve() 135 | .then(() => git('fetch', remote.name)) 136 | .then(() => git('reset', `${remote.name}/master`, '--hard')) 137 | .then(() => git('clean', '--force')) 138 | ) 139 | .catch(() => Promise.resolve()) 140 | ) 141 | .then(() => run('build')) 142 | .then(() => git('add', '.', '--all')) 143 | .then(() => git('commit', '--message', new Date().toUTCString()) 144 | .catch(() => Promise.resolve())) 145 | .then(() => git('push', remote.name, 'master', '--force', '--set-upstream')); 146 | }); 147 | 148 | // 149 | // Build website and launch it in a browser for testing in watch mode 150 | // ----------------------------------------------------------------------------- 151 | tasks.set('start', () => { 152 | global.HMR = !process.argv.includes('--no-hmr'); // Hot Module Replacement (HMR) 153 | return Promise.resolve() 154 | .then(() => run('clean')) 155 | .then(() => run('appsettings')) 156 | .then(() => new Promise(resolve => { 157 | let count = 0; 158 | const webpackConfig = require('./webpack.config'); 159 | const compiler = webpack(webpackConfig); 160 | // Node.js middleware that compiles application in watch mode with HMR support 161 | // http://webpack.github.io/docs/webpack-dev-middleware.html 162 | const webpackDevMiddleware = require('webpack-dev-middleware')(compiler, { 163 | publicPath: webpackConfig.output.publicPath, 164 | stats: webpackConfig.stats, 165 | }); 166 | compiler.plugin('done', () => { 167 | // Launch ASP.NET Core server after the initial bundling is complete 168 | if (++count === 1) { 169 | const options = { 170 | cwd: path.resolve(__dirname, './server/'), 171 | stdio: ['ignore', 'pipe', 'inherit'], 172 | env: Object.assign({}, process.env, { 173 | ASPNETCORE_ENVIRONMENT: 'Development', 174 | }), 175 | }; 176 | cp.spawn('dotnet', ['watch', 'run'], options).stdout.on('data', data => { 177 | process.stdout.write(data); 178 | if (data.indexOf('Application started.') !== -1) { 179 | // Launch Browsersync after the initial bundling is complete 180 | // For more information visit https://browsersync.io/docs/options 181 | require('browser-sync').create().init({ 182 | proxy: { 183 | target: 'localhost:5000', 184 | middleware: [ 185 | webpackDevMiddleware, 186 | require('webpack-hot-middleware')(compiler), 187 | ], 188 | }, 189 | }, resolve); 190 | } 191 | }); 192 | } 193 | }); 194 | })); 195 | }); 196 | 197 | // Execute the specified task or default one. E.g.: node run build 198 | run(/^\w/.test(process.argv[2] || '') ? process.argv[2] : 'start' /* default */); 199 | -------------------------------------------------------------------------------- /server.test/SampleTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using Xunit; 6 | 7 | namespace server.test 8 | { 9 | // see example explanation on xUnit.net website: 10 | // https://xunit.github.io/docs/getting-started-dotnet-core.html 11 | public class SampleTest 12 | { 13 | [Fact] 14 | public void PassingTest() 15 | { 16 | Assert.Equal(4, Add(2, 2)); 17 | } 18 | 19 | [Fact] 20 | public void FailingTest() 21 | { 22 | Assert.Equal(5, Add(2, 2)); 23 | } 24 | 25 | int Add(int x, int y) 26 | { 27 | return x + y; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server.test/server.test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp1.1 5 | true 6 | server.test 7 | server.test 8 | true 9 | 1.1.1 10 | $(PackageTargetFallback);dotnet5.6;dnxcore50;portable-net45+win8 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /server/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using System.Collections.Generic; 6 | 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Identity; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.Extensions.Logging; 11 | using Server.Models; 12 | 13 | namespace Server.Controllers 14 | { 15 | [Authorize] 16 | public class AccountController : Controller 17 | { 18 | private readonly SignInManager _signInManager; 19 | private readonly ILogger _logger; 20 | private readonly SortedList _providers; 21 | 22 | public AccountController(SignInManager signInManager, ILoggerFactory loggerFactory) 23 | { 24 | _signInManager = signInManager; 25 | _logger = loggerFactory.CreateLogger(); 26 | _providers = new SortedList 27 | { 28 | { "facebook", "Facebook" } 29 | }; 30 | } 31 | 32 | [HttpGet("login/{provider}")] 33 | [AllowAnonymous] 34 | public IActionResult ExternalLogin(string provider, string returnUrl = null) 35 | { 36 | // Request a redirect to the external login provider. 37 | var providerName = _providers.ContainsKey(provider) ? _providers[provider] : provider; 38 | var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); 39 | var properties = _signInManager.ConfigureExternalAuthenticationProperties(providerName, redirectUrl); 40 | return Challenge(properties, providerName); 41 | } 42 | 43 | [HttpGet("auth")] 44 | [AllowAnonymous] 45 | public IActionResult ExternalLoginCallback(string returnUrl = null, string remoteError = null) 46 | { 47 | // TODO: Handle 3rd-party authentication callback 48 | return Content("Account Controller => Login Callback", "text/plain"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | 8 | using Microsoft.AspNetCore.Antiforgery; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.AspNetCore.Http; 11 | using Microsoft.AspNetCore.Mvc; 12 | using Newtonsoft.Json; 13 | 14 | namespace Server.Controllers 15 | { 16 | public class HomeController : Controller 17 | { 18 | private readonly IHostingEnvironment _env; 19 | private readonly IAntiforgery _antiforgery; 20 | private dynamic _assets; 21 | 22 | public HomeController(IHostingEnvironment env, IAntiforgery antiforgery) 23 | { 24 | _env = env; 25 | _antiforgery = antiforgery; 26 | } 27 | 28 | public async Task Index() 29 | { 30 | // Get the filename of the JavaScript bundle generated by Webpack 31 | if (_env.IsDevelopment() || _assets == null) 32 | { 33 | var assetsFileName = Path.Combine(_env.WebRootPath, "./dist/assets.json"); 34 | 35 | using (var stream = System.IO.File.OpenRead(assetsFileName)) 36 | using (var reader = new StreamReader(stream)) 37 | { 38 | var json = await reader.ReadToEndAsync(); 39 | _assets = JsonConvert.DeserializeObject(json); 40 | } 41 | } 42 | 43 | ViewData["assets:main:js"] = (string)_assets.main.js; 44 | 45 | // Send the request token as a JavaScript-readable cookie 46 | var tokens = _antiforgery.GetAndStoreTokens(Request.HttpContext); 47 | Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false }); 48 | 49 | return View(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/Models/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | namespace Server.Models 9 | { 10 | public class DatabaseContext : IdentityDbContext 11 | { 12 | public DatabaseContext(DbContextOptions options) : base(options) { } 13 | 14 | protected override void OnModelCreating(ModelBuilder builder) 15 | { 16 | base.OnModelCreating(builder); 17 | // Customize the ASP.NET Identity model and override the defaults if needed. 18 | // For example, you can rename the ASP.NET Identity table names and more. 19 | // Add your customizations after calling base.OnModelCreating(builder); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/Models/User.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 6 | 7 | namespace Server.Models 8 | { 9 | public class User : IdentityUser 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright © 2014-present Kriasoft, LLC. All rights reserved. 2 | // This source code is licensed under the MIT license found in the 3 | // LICENSE.txt file in the root directory of this source tree. 4 | 5 | using System.IO; 6 | 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 10 | using Microsoft.EntityFrameworkCore; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | using Server.Models; 15 | 16 | namespace Server 17 | { 18 | public class Startup 19 | { 20 | // Load application settings from JSON file(s) 21 | // https://docs.asp.net/en/latest/fundamentals/configuration.html 22 | public Startup(IHostingEnvironment env) 23 | { 24 | Configuration = new ConfigurationBuilder() 25 | .SetBasePath(env.ContentRootPath) 26 | .AddJsonFile($"appsettings.json", optional: true) 27 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 28 | .Build(); 29 | } 30 | 31 | public IConfiguration Configuration { get; set; } 32 | 33 | // Configure IoC container 34 | // https://docs.asp.net/en/latest/fundamentals/dependency-injection.html 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | // https://docs.asp.net/en/latest/security/anti-request-forgery.html 38 | services.AddAntiforgery(options => options.CookieName = options.HeaderName = "X-XSRF-TOKEN"); 39 | 40 | // Register Entity Framework database context 41 | // https://docs.efproject.net/en/latest/platforms/aspnetcore/new-db.html 42 | services.AddDbContext(options => 43 | { 44 | options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); 45 | }); 46 | 47 | services.AddIdentity() 48 | .AddEntityFrameworkStores() 49 | .AddDefaultTokenProviders(); 50 | 51 | services.AddMvcCore() 52 | .AddAuthorization() 53 | .AddViews() 54 | .AddRazorViewEngine() 55 | .AddJsonFormatters(); 56 | } 57 | 58 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory) 59 | { 60 | // Configure logging 61 | // https://docs.asp.net/en/latest/fundamentals/logging.html 62 | factory.AddConsole(Configuration.GetSection("Logging")); 63 | factory.AddDebug(); 64 | 65 | // Serve static files 66 | // https://docs.asp.net/en/latest/fundamentals/static-files.html 67 | app.UseStaticFiles(); 68 | 69 | // Enable external authentication provider(s) 70 | // https://docs.asp.net/en/latest/security/authentication/sociallogins.html 71 | app.UseIdentity(); 72 | 73 | if (!string.IsNullOrEmpty(Configuration["Authentication:Facebook:AppId"])) 74 | { 75 | app.UseFacebookAuthentication(new FacebookOptions 76 | { 77 | AppId = Configuration["Authentication:Facebook:AppId"], 78 | AppSecret = Configuration["Authentication:Facebook:AppSecret"], 79 | Scope = { "email" }, 80 | Fields = { "name", "email" }, 81 | SaveTokens = true, 82 | }); 83 | } 84 | 85 | // Configure ASP.NET MVC 86 | // https://docs.asp.net/en/latest/mvc/index.html 87 | app.UseMvc(routes => 88 | { 89 | routes.MapRoute("default", "{*url}", new { controller = "Home", action = "Index" }); 90 | }); 91 | } 92 | 93 | public static void Main() 94 | { 95 | var cwd = Directory.GetCurrentDirectory(); 96 | var web = Path.GetFileName(cwd) == "server" ? "../public" : "public"; 97 | 98 | var host = new WebHostBuilder() 99 | .UseContentRoot(Directory.GetCurrentDirectory()) 100 | .UseWebRoot(web) 101 | .UseKestrel() 102 | .UseIISIntegration() 103 | .UseStartup() 104 | .Build(); 105 | 106 | host.Run(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /server/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ASP.NET Core Starter Kit 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=AspNetCoreStarterKit;Trusted_Connection=True;MultipleActiveResultSets=true" 4 | }, 5 | 6 | "Logging": { 7 | "IncludeScopes": false, 8 | "LogLevel": { 9 | "Default": "Debug", 10 | "System": "Information", 11 | "Microsoft": "Information" 12 | } 13 | }, 14 | 15 | "Authentication": { 16 | "Facebook": { 17 | "AppId": "", 18 | "AppSecret": "" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp1.1 5 | portable 6 | true 7 | server 8 | Exe 9 | server 10 | 1.1.1 11 | $(PackageTargetFallback);dnxcore50;portable-net45+win8 12 | 13 | 14 | 15 | 16 | PreserveNewest 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | All 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /server/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /starter-kit.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\server.csproj", "{F951ECB5-132F-4A28-8DA7-66E35356344B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Test", "server.test\server.test.csproj", "{D9762B10-6A14-4464-9141-66DC196545C0}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F951ECB5-132F-4A28-8DA7-66E35356344B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F951ECB5-132F-4A28-8DA7-66E35356344B}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F951ECB5-132F-4A28-8DA7-66E35356344B}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F951ECB5-132F-4A28-8DA7-66E35356344B}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {D9762B10-6A14-4464-9141-66DC196545C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D9762B10-6A14-4464-9141-66DC196545C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D9762B10-6A14-4464-9141-66DC196545C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D9762B10-6A14-4464-9141-66DC196545C0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ASP.NET Core Starter Kit (https://dotnetreact.com) 3 | * 4 | * Copyright © 2014-present Kriasoft, LLC. All rights reserved. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | /* eslint-disable global-require */ 11 | 12 | const path = require('path'); 13 | const webpack = require('webpack'); 14 | const AssetsPlugin = require('assets-webpack-plugin'); 15 | const pkg = require('./package.json'); 16 | 17 | const isDebug = global.DEBUG === false ? false : !process.argv.includes('--release'); 18 | const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v'); 19 | const useHMR = !!global.HMR; // Hot Module Replacement (HMR) 20 | const babelConfig = Object.assign({}, pkg.babel, { 21 | babelrc: false, 22 | cacheDirectory: useHMR, 23 | }); 24 | 25 | // Webpack configuration (client/main.js => public/dist/main.{hash}.js) 26 | // http://webpack.github.io/docs/configuration.html 27 | const config = { 28 | 29 | // The base directory for resolving the entry option 30 | context: path.resolve(__dirname, './client'), 31 | 32 | // The entry point for the bundle 33 | entry: [ 34 | /* Material Design Lite (https://getmdl.io) */ 35 | '!!style-loader!css-loader!react-mdl/extra/material.min.css', 36 | 'react-mdl/extra/material.min.js', 37 | /* The main entry point of your JavaScript application */ 38 | './main.js', 39 | ], 40 | 41 | // Options affecting the output of the compilation 42 | output: { 43 | path: path.resolve(__dirname, './public/dist'), 44 | publicPath: '/dist/', 45 | filename: isDebug ? '[name].js?[hash]' : '[name].[hash].js', 46 | chunkFilename: isDebug ? '[id].js?[chunkhash]' : '[id].[chunkhash].js', 47 | sourcePrefix: ' ', 48 | }, 49 | 50 | // Developer tool to enhance debugging, source maps 51 | // http://webpack.github.io/docs/configuration.html#devtool 52 | devtool: isDebug ? 'source-map' : false, 53 | 54 | // What information should be printed to the console 55 | stats: { 56 | colors: true, 57 | reasons: isDebug, 58 | hash: isVerbose, 59 | version: isVerbose, 60 | timings: true, 61 | chunks: isVerbose, 62 | chunkModules: isVerbose, 63 | cached: isVerbose, 64 | cachedAssets: isVerbose, 65 | }, 66 | 67 | // The list of plugins for Webpack compiler 68 | plugins: [ 69 | new webpack.optimize.OccurrenceOrderPlugin(), 70 | new webpack.DefinePlugin({ 71 | 'process.env.NODE_ENV': isDebug ? '"development"' : '"production"', 72 | __DEV__: isDebug, 73 | }), 74 | // Emit a JSON file with assets paths 75 | // https://github.com/sporto/assets-webpack-plugin#options 76 | new AssetsPlugin({ 77 | path: path.resolve(__dirname, './public/dist'), 78 | filename: 'assets.json', 79 | prettyPrint: true, 80 | }), 81 | ], 82 | // Options affecting the normal modules 83 | module: { 84 | rules: [ 85 | { 86 | test: /\.jsx?$/, 87 | include: [ 88 | path.resolve(__dirname, './client'), 89 | ], 90 | loader: `babel-loader?${JSON.stringify(babelConfig)}`, 91 | }, 92 | { 93 | test: /\.css/, 94 | loaders: [ 95 | 'style-loader', 96 | `css-loader?${JSON.stringify({ 97 | sourceMap: isDebug, 98 | // CSS Modules https://github.com/css-modules/css-modules 99 | modules: true, 100 | localIdentName: isDebug ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]', 101 | // CSS Nano http://cssnano.co/options/ 102 | minimize: !isDebug, 103 | })}`, 104 | //https://github.com/postcss/postcss-loader/issues/209 105 | {loader: 'postcss-loader', options: { plugins: () => [ 106 | // Transfer @import rule by inlining content, e.g. @import 'normalize.css' 107 | // https://github.com/postcss/postcss-import 108 | // require('postcss-import')({ addDependencyTo: bundler }), 109 | // W3C variables, e.g. :root { --color: red; } div { background: var(--color); } 110 | // https://github.com/postcss/postcss-custom-properties 111 | require('postcss-custom-properties'), 112 | // W3C CSS Custom Media Queries, e.g. @custom-media --small-viewport (max-width: 30em); 113 | // https://github.com/postcss/postcss-custom-media 114 | require('postcss-custom-media'), 115 | // CSS4 Media Queries, e.g. @media screen and (width >= 500px) and (width <= 1200px) { } 116 | // https://github.com/postcss/postcss-media-minmax 117 | require('postcss-media-minmax'), 118 | // W3C CSS Custom Selectors, e.g. @custom-selector :--heading h1, h2, h3, h4, h5, h6; 119 | // https://github.com/postcss/postcss-custom-selectors 120 | require('postcss-custom-selectors'), 121 | // W3C calc() function, e.g. div { height: calc(100px - 2em); } 122 | // https://github.com/postcss/postcss-calc 123 | require('postcss-calc'), 124 | // Allows you to nest one style rule inside another 125 | // https://github.com/jonathantneal/postcss-nesting 126 | require('postcss-nesting'), 127 | // W3C color() function, e.g. div { background: color(red alpha(90%)); } 128 | // https://github.com/postcss/postcss-color-function 129 | require('postcss-color-function'), 130 | // Convert CSS shorthand filters to SVG equivalent, e.g. .blur { filter: blur(4px); } 131 | // https://github.com/iamvdo/pleeease-filters 132 | require('pleeease-filters'), 133 | // Generate pixel fallback for "rem" units, e.g. div { margin: 2.5rem 2px 3em 100%; } 134 | // https://github.com/robwierzbowski/node-pixrem 135 | require('pixrem'), 136 | // W3C CSS Level4 :matches() pseudo class, e.g. p:matches(:first-child, .special) { } 137 | // https://github.com/postcss/postcss-selector-matches 138 | require('postcss-selector-matches'), 139 | // Transforms :not() W3C CSS Level 4 pseudo class to :not() CSS Level 3 selectors 140 | // https://github.com/postcss/postcss-selector-not 141 | require('postcss-selector-not'), 142 | // Add vendor prefixes to CSS rules using values from caniuse.com 143 | // https://github.com/postcss/autoprefixer 144 | require('autoprefixer') 145 | ] }}, 146 | 147 | ], 148 | }, 149 | { 150 | test: /\.json$/, 151 | exclude: [ 152 | path.resolve(__dirname, './client/routes.json'), 153 | ], 154 | loader: 'json-loader', 155 | }, 156 | { 157 | test: /\.json$/, 158 | include: [ 159 | path.resolve(__dirname, './client/routes.json'), 160 | ], 161 | loaders: [ 162 | `babel-loader?${JSON.stringify(babelConfig)}`, 163 | path.resolve(__dirname, './client/utils/routes-loader.js'), 164 | ], 165 | }, 166 | { 167 | test: /\.md$/, 168 | loader: path.resolve(__dirname, './client/utils/markdown-loader.js'), 169 | }, 170 | { 171 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)(\?.*)$/, 172 | loader: 'url-loader?limit=10000', 173 | }, 174 | { 175 | test: /\.(eot|ttf|wav|mp3)(\?.*)$/, 176 | loader: 'file-loader', 177 | }, 178 | ], 179 | }, 180 | }; 181 | 182 | // Integrate Webpack 2.x (disable ES2015 modules) 183 | babelConfig.presets[babelConfig.presets.indexOf('latest')] = ['latest', { 184 | es2015: { 185 | modules: false, 186 | }, 187 | }]; 188 | 189 | // Optimize the bundle in release (production) mode 190 | if (!isDebug) { 191 | config.plugins.push(new webpack.optimize.UglifyJsPlugin({ 192 | compress: { 193 | warnings: isVerbose, 194 | }, 195 | })); 196 | config.plugins.push(new webpack.optimize.AggressiveMergingPlugin()); 197 | } 198 | 199 | // Hot Module Replacement (HMR) + React Hot Reload 200 | if (isDebug && useHMR) { 201 | babelConfig.plugins.unshift('react-hot-loader/babel'); 202 | config.entry.unshift('react-hot-loader/patch', 'webpack-hot-middleware/client'); 203 | config.plugins.push(new webpack.HotModuleReplacementPlugin()); 204 | config.plugins.push(new webpack.NoEmitOnErrorsPlugin()); 205 | } 206 | 207 | module.exports = config; 208 | --------------------------------------------------------------------------------