├── ClientApp ├── .npmrc ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── img │ │ └── icons │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── mstile-150x150.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon-60x60.png │ │ │ ├── apple-touch-icon-76x76.png │ │ │ ├── apple-touch-icon-120x120.png │ │ │ ├── apple-touch-icon-152x152.png │ │ │ ├── apple-touch-icon-180x180.png │ │ │ ├── msapplication-icon-144x144.png │ │ │ ├── android-chrome-maskable-192x192.png │ │ │ ├── android-chrome-maskable-512x512.png │ │ │ └── safari-pinned-tab.svg │ ├── manifest.json │ └── index.html ├── .browserslistrc ├── src │ ├── store │ │ ├── types.ts │ │ ├── counter │ │ │ ├── types.ts │ │ │ ├── getters.ts │ │ │ ├── mutations.ts │ │ │ ├── actions.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── shims-vue.d.ts │ ├── assets │ │ └── logo.png │ ├── shims-vuetify.d.ts │ ├── filters │ │ └── date.filter.ts │ ├── models │ │ └── Forecast.ts │ ├── shims-tsx.d.ts │ ├── main.ts │ ├── views │ │ ├── Counter.vue │ │ ├── FetchData.vue │ │ ├── FetchData-decorator.vue │ │ └── Home.vue │ ├── components │ │ ├── HelloWorld.vue │ │ └── Counter.vue │ ├── router │ │ └── index.ts │ ├── plugins │ │ ├── vuetify.ts │ │ └── axios.ts │ ├── registerServiceWorker.ts │ └── App.vue ├── babel.config.js ├── postcss.config.js ├── .prettierrc ├── vue.config.js ├── .gitignore ├── README.md ├── tsconfig.json ├── .eslintrc.js ├── package.md └── package.json ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── screenshot.png ├── .eslintrc.js ├── Pages ├── _ViewImports.cshtml ├── Error.cshtml.cs └── Error.cshtml ├── vetur.config.js ├── appsettings.Development.json ├── appsettings.json ├── Models └── WeatherForecast.cs ├── Dockerfile ├── Program.cs ├── SoftwareAteliers.AspNetCoreVueStarter.nuspec ├── .template.config └── template.json ├── AspNetCoreVueStarter.sln ├── Controllers └── WeatherForecastController.cs ├── .gitattributes ├── AspNetCoreVueStarter.csproj ├── Startup.cs ├── .gitignore ├── CommandLine.cs └── README.md /ClientApp/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /ClientApp/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /ClientApp/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [{ "mode": "auto" }] 3 | } -------------------------------------------------------------------------------- /ClientApp/src/store/types.ts: -------------------------------------------------------------------------------- 1 | export interface RootState { 2 | version: string 3 | } 4 | -------------------------------------------------------------------------------- /ClientApp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/cli-plugin-babel/preset'] 3 | } 4 | -------------------------------------------------------------------------------- /ClientApp/src/store/counter/types.ts: -------------------------------------------------------------------------------- 1 | export interface CounterState { 2 | counter: number 3 | } 4 | -------------------------------------------------------------------------------- /ClientApp/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/screenshot.png -------------------------------------------------------------------------------- /ClientApp/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 'extends': [ 4 | './ClientApp/.eslintrc.js' 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /ClientApp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/favicon.ico -------------------------------------------------------------------------------- /ClientApp/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/src/assets/logo.png -------------------------------------------------------------------------------- /ClientApp/src/shims-vuetify.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vuetify/lib/framework' { 2 | import Vuetify from 'vuetify' 3 | export default Vuetify 4 | } 5 | -------------------------------------------------------------------------------- /Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AspNetCoreVueStarter 2 | @namespace AspNetCoreVueStarter.Pages 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /ClientApp/public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /ClientApp/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "endOfLine": "auto" 8 | } -------------------------------------------------------------------------------- /ClientApp/public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /ClientApp/src/filters/date.filter.ts: -------------------------------------------------------------------------------- 1 | import { format } from 'date-fns' 2 | 3 | export default (date: Date): string => { 4 | return format(new Date(date), 'eeee, dd MMMM') 5 | } 6 | -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /vetur.config.js: -------------------------------------------------------------------------------- 1 | // vetur.config.js 2 | /** @type {import('vls').VeturConfig} */ 3 | module.exports = { 4 | settings: {}, 5 | projects: [ 6 | './ClientApp' 7 | ] 8 | } -------------------------------------------------------------------------------- /ClientApp/public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /ClientApp/src/models/Forecast.ts: -------------------------------------------------------------------------------- 1 | export class Forecast { 2 | public date?: Date 3 | public temperatureC?: number 4 | public temperatureF?: number 5 | public summary?: string 6 | } 7 | -------------------------------------------------------------------------------- /ClientApp/public/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /ClientApp/public/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SoftwareAteliers/asp-net-core-vue-starter/HEAD/ClientApp/public/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ClientApp/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureWebpack: { 3 | devtool: 'source-map' 4 | }, 5 | transpileDependencies: ['vuetify'], 6 | devServer: { 7 | progress: !!process.env.VUE_DEV_SERVER_PROGRESS 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /ClientApp/public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ClientApp/src/store/counter/getters.ts: -------------------------------------------------------------------------------- 1 | import { GetterTree } from 'vuex' 2 | import { CounterState } from './types' 3 | import { RootState } from '../types' 4 | 5 | export const getters: GetterTree = { 6 | currentCount(state): number { 7 | return state.counter 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ClientApp/src/store/counter/mutations.ts: -------------------------------------------------------------------------------- 1 | import { MutationTree } from 'vuex' 2 | import { CounterState } from './types' 3 | 4 | export const mutations: MutationTree = { 5 | incrementCounter(state) { 6 | state.counter++ 7 | }, 8 | resetCounter(state) { 9 | state.counter = 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ClientApp/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /ClientApp/src/store/counter/actions.ts: -------------------------------------------------------------------------------- 1 | import { ActionTree } from 'vuex' 2 | import { CounterState } from './types' 3 | import { RootState } from '../types' 4 | 5 | export const actions: ActionTree = { 6 | increment({ commit }) { 7 | commit('incrementCounter') 8 | }, 9 | reset({ commit }) { 10 | commit('resetCounter') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ClientApp/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Models/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspNetCoreVueStarter.Models 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ClientApp/README.md: -------------------------------------------------------------------------------- 1 | # clientapp 2 | 3 | ## Project setup 4 | ``` 5 | pnpm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | pnpm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | pnpm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | pnpm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /ClientApp/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import './plugins/axios' 3 | import vuetify from './plugins/vuetify' 4 | import App from './App.vue' 5 | import router from './router' 6 | import store from '@/store/index' 7 | import './registerServiceWorker' 8 | import dateFilter from '@/filters/date.filter' 9 | 10 | Vue.config.productionTip = false 11 | 12 | Vue.filter('date', dateFilter) 13 | 14 | new Vue({ 15 | vuetify, 16 | router, 17 | store, 18 | render: (h) => h(App) 19 | }).$mount('#app') 20 | -------------------------------------------------------------------------------- /ClientApp/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex, { StoreOptions } from 'vuex' 3 | import { RootState } from './types' 4 | import { counter } from './counter/index' 5 | 6 | Vue.use(Vuex) 7 | 8 | // Vuex structure based on https://codeburst.io/vuex-and-typescript-3427ba78cfa8 9 | 10 | const store: StoreOptions = { 11 | state: { 12 | version: '1.0.0' // a simple property 13 | }, 14 | modules: { 15 | counter 16 | } 17 | } 18 | 19 | export default new Vuex.Store(store) 20 | -------------------------------------------------------------------------------- /ClientApp/src/store/counter/index.ts: -------------------------------------------------------------------------------- 1 | import { Module } from 'vuex' 2 | import { getters } from './getters' 3 | import { actions } from './actions' 4 | import { mutations } from './mutations' 5 | import { CounterState } from './types' 6 | import { RootState } from '../types' 7 | 8 | export const state: CounterState = { 9 | counter: 0 10 | } 11 | 12 | const namespaced = true 13 | 14 | export const counter: Module = { 15 | namespaced, 16 | state, 17 | getters, 18 | actions, 19 | mutations 20 | } 21 | -------------------------------------------------------------------------------- /ClientApp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp-net-core-vue-starter", 3 | "short_name": "asp-net-core-vue-starter", 4 | "icons": [ 5 | { 6 | "src": "/img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "/index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /ClientApp/src/views/Counter.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | -------------------------------------------------------------------------------- /ClientApp/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 24 | 25 | 41 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | 5 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 6 | WORKDIR /src 7 | COPY ["./AspNetCoreVueStarter.csproj", "app/"] 8 | RUN dotnet restore "app/AspNetCoreVueStarter.csproj" 9 | COPY . . 10 | RUN apt-get update -yq 11 | RUN apt-get install curl gnupg -yq 12 | RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - 13 | RUN apt-get install -y nodejs 14 | RUN dotnet build "/src/AspNetCoreVueStarter.csproj" -c Release -o /app/build 15 | 16 | FROM build AS publish 17 | RUN dotnet publish "/src/AspNetCoreVueStarter.csproj" -c Release -o /app/publish 18 | 19 | FROM base AS runtime 20 | WORKDIR /app 21 | COPY --from=publish /app/publish . 22 | ENTRYPOINT ["dotnet", "AspNetCoreVueStarter.dll"] 23 | -------------------------------------------------------------------------------- /ClientApp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /ClientApp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true 4 | }, 5 | /* ES Lint Standard config 6 | extends: [ 7 | 'plugin:vue/essential', 8 | '@vue/standard', 9 | '@vue/typescript/recommended' 10 | ], 11 | */ 12 | /* ESLint + Prettier config */ 13 | extends: [ 14 | 'plugin:vue/essential', 15 | 'eslint:recommended', 16 | '@vue/typescript/recommended', 17 | '@vue/prettier', 18 | '@vue/prettier/@typescript-eslint' 19 | ], 20 | parserOptions: { 21 | ecmaVersion: 2020 22 | }, 23 | rules: { 24 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 26 | 'space-before-function-paren': [ 27 | 'error', 28 | { 29 | anonymous: 'always', 30 | named: 'never', 31 | asyncArrow: 'always' 32 | } 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ClientApp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ClientApp/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter, { RouteConfig } from 'vue-router' 3 | import Home from '../views/Home.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes: Array = [ 8 | { 9 | path: '/', 10 | name: 'Home', 11 | component: Home 12 | }, 13 | { 14 | path: '/counter', 15 | name: 'counter', 16 | // route level code-splitting 17 | // this generates a separate chunk (about.[hash].js) for this route 18 | // which is lazy-loaded when the route is visited. 19 | component: () => import(/* webpackChunkName: "counter" */ '../views/Counter.vue') 20 | }, 21 | { 22 | path: '/fetch-data', 23 | name: 'fetch-data', 24 | component: () => import(/* webpackChunkName: "fetch-data" */ '../views/FetchData.vue') 25 | } 26 | ] 27 | 28 | const router = new VueRouter({ 29 | mode: 'history', 30 | routes 31 | }) 32 | 33 | export default router 34 | -------------------------------------------------------------------------------- /Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AspNetCoreVueStarter.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | public class ErrorModel : PageModel 14 | { 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public string RequestId { get; set; } 23 | 24 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 25 | 26 | public void OnGet() 27 | { 28 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model ErrorModel 3 | @{ 4 | ViewData["Title"] = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | @if (Model.ShowRequestId) 11 | { 12 |

13 | Request ID: @Model.RequestId 14 |

15 | } 16 | 17 |

Development Mode

18 |

19 | Swapping to the Development environment displays detailed information about the error that occurred. 20 |

21 |

22 | The Development environment shouldn't be enabled for deployed applications. 23 | It can result in displaying sensitive information from exceptions to end users. 24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 25 | and restarting the app. 26 |

27 | -------------------------------------------------------------------------------- /ClientApp/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import '@mdi/font/css/materialdesignicons.css' 2 | import Vue from 'vue' 3 | import Vuetify, { 4 | VAlert, 5 | VApp, 6 | VNavigationDrawer, 7 | VFooter, 8 | VList, 9 | VBtn, 10 | VIcon, 11 | VToolbar, 12 | VDataTable, 13 | VProgressLinear 14 | } from 'vuetify/lib' 15 | 16 | // vue-cli a-la-carte installation 17 | Vue.use(Vuetify, { 18 | components: { 19 | VAlert, 20 | VApp, 21 | VNavigationDrawer, 22 | VFooter, 23 | VList, 24 | VBtn, 25 | VIcon, 26 | VToolbar, 27 | VDataTable, 28 | VProgressLinear 29 | } 30 | }) 31 | 32 | const opts = { 33 | theme: { 34 | themes: { 35 | light: { 36 | primary: '#1976D2', 37 | secondary: '#424242', 38 | accent: '#82B1FF', 39 | error: '#FF5252', 40 | info: '#2196F3', 41 | success: '#4CAF50' 42 | } 43 | // dark: { 44 | // } 45 | } 46 | } 47 | } 48 | 49 | export default new Vuetify(opts) 50 | -------------------------------------------------------------------------------- /ClientApp/src/components/Counter.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 37 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Hosting; 4 | using VueCliMiddleware; 5 | 6 | namespace AspNetCoreVueStarter 7 | { 8 | public class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | if (!CommandLine.Arguments.TryGetOptions(args, true, out string mode, out ushort port, out bool https)) return; 13 | 14 | if (mode == "kill") { 15 | Console.WriteLine($"Killing process serving port {port}..."); 16 | PidUtils.KillPort(port, true, true); 17 | return; 18 | } 19 | 20 | CreateHostBuilder(args).Build().Run(); 21 | } 22 | 23 | public static IHostBuilder CreateHostBuilder(string[] args) => 24 | Host.CreateDefaultBuilder(args) 25 | .ConfigureWebHostDefaults(webBuilder => 26 | { 27 | webBuilder.UseStartup(); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ClientApp/src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered() { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached() { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound() { 20 | console.log('New content is downloading.') 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline() { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error(error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /SoftwareAteliers.AspNetCoreVueStarter.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SoftwareAteliers.AspNetCoreVueStarter 5 | 3.1.2 6 | 7 | ASP.NET Core + Vue.js starter project 8 | The repository contains an ASP.NET Core + Vue.js starter template. The template runs on ASP.NET Core 5.0 and is created by Vue CLI 4.0 with a new plugin based architecture allowing developers to interactively scaffold a new project with just a one command. 9 | 10 | SoftwareAteliers 11 | https://github.com/SoftwareAteliers/asp-net-core-vue-starter 12 | MIT 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/template", 3 | "author": "SoftwareAteliers", 4 | "classifications": ["Web", "SPA", "Vue.js"], 5 | "identity": "SoftwareAteliers.AspNetCoreVueStarter", 6 | "name": ".NET Core Vue.js", 7 | "shortName": "vue", 8 | "sourceName": "AspNetCoreVueStarter", 9 | "preferNameDirectory": false, 10 | "primaryOutputs": [{ "path": "AspNetCoreVueStarter.csproj" }], 11 | "sources": [ 12 | { 13 | "source": "./", 14 | "target": "./", 15 | "exclude": [".template.config/**"] 16 | } 17 | ], 18 | "symbols": { 19 | "skipBuild": { 20 | "type": "parameter", 21 | "datatype": "bool", 22 | "description": "If specified, skips the automatic build of the project on create.", 23 | "defaultValue": "false" 24 | } 25 | }, 26 | "postActions": [ 27 | { 28 | "condition": "(!skipBuild)", 29 | "description": "Restore, then build this project.", 30 | "manualInstructions": [ 31 | { 32 | "text": "Run 'dotnet build'" 33 | } 34 | ], 35 | "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", 36 | "continueOnError": true 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /AspNetCoreVueStarter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreVueStarter", "AspNetCoreVueStarter.csproj", "{D1A4B196-FA92-4822-8C25-07D53A2903EF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {D1A4B196-FA92-4822-8C25-07D53A2903EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {D1A4B196-FA92-4822-8C25-07D53A2903EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {D1A4B196-FA92-4822-8C25-07D53A2903EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {D1A4B196-FA92-4822-8C25-07D53A2903EF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {FEBD4BD6-299C-48F2-8D24-13F9B53219E2} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ClientApp/package.md: -------------------------------------------------------------------------------- 1 | # Package Alternatives 2 | This file contains references for changing out parts of package.json depending on what features you want 3 | ## ESLint / Prettier Options 4 | - See comments in eslintrc.js 5 | - Also recommend editor.config so VS Code Prettier doesn't just pickup Code's settings 6 | - Whole changed sections below for ease of copying and pasting 7 | 8 | ### Original ESLint / Standard 9 | ```json 10 | "@vue/eslint-config-standard": "^5.1.2", 11 | "@vue/eslint-config-typescript": "^7.0.0", 12 | "eslint": "^6.7.2", 13 | "eslint-plugin-import": "^2.20.2", 14 | "eslint-plugin-node": "^11.1.0", 15 | "eslint-plugin-promise": "^4.2.1", 16 | "eslint-plugin-standard": "^4.0.0", 17 | "eslint-plugin-vue": "^6.2.2", 18 | "node-sass": "^5.0.0", 19 | ``` 20 | 21 | ### Prettier Alternative 22 | ```json 23 | "@vue/eslint-config-prettier": "^6.0.0", 24 | "@vue/eslint-config-typescript": "^7.0.0", 25 | "eslint": "^6.7.2", 26 | "eslint-plugin-prettier": "^3.3.1", 27 | "eslint-plugin-vue": "^6.2.2", 28 | "node-sass": "^5.0.0", 29 | "prettier": "^2.2.1", 30 | ``` 31 | 32 | *[Idea for package.md](https://spin.atomicobject.com/2019/05/20/document-package-json/)* 33 | -------------------------------------------------------------------------------- /Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using AspNetCoreVueStarter.Models; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace AspNetCoreVueStarter.Controllers 10 | { 11 | [ApiController] 12 | [Route("api/[controller]")] 13 | public class WeatherForecastController : ControllerBase 14 | { 15 | private static readonly string[] Summaries = new[] 16 | { 17 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 18 | }; 19 | 20 | private readonly ILogger _logger; 21 | 22 | public WeatherForecastController(ILogger logger) 23 | { 24 | _logger = logger; 25 | } 26 | 27 | [HttpGet] 28 | public IEnumerable Get() 29 | { 30 | var rng = new Random(); 31 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast 32 | { 33 | Date = DateTime.Now.AddDays(index), 34 | TemperatureC = rng.Next(-20, 55), 35 | Summary = Summaries[rng.Next(Summaries.Length)] 36 | }) 37 | .ToArray(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ClientApp/src/plugins/axios.ts: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import Vue from 'vue' 4 | import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' 5 | 6 | // Full config: https://github.com/axios/axios#request-config 7 | // axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || ''; 8 | // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; 9 | // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; 10 | 11 | const config = { 12 | // baseURL: process.env.baseURL || process.env.apiUrl || "" 13 | // timeout: 60 * 1000, // Timeout 14 | // withCredentials: true, // Check cross-site Access-Control 15 | } 16 | 17 | // tslint:disable-next-line: variable-name 18 | const _axios = axios.create(config) 19 | 20 | _axios.interceptors.request.use( 21 | // Do something before request is sent 22 | (conf: AxiosRequestConfig) => conf, 23 | // Do something with request error 24 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 25 | (error: any) => Promise.reject(error) 26 | ) 27 | 28 | // Add a response interceptor 29 | _axios.interceptors.response.use( 30 | // Do something with response data 31 | (response) => response, 32 | // Do something with response error 33 | (error) => Promise.reject(error) 34 | ) 35 | 36 | function AxiosPlugin(vue: typeof Vue): void { 37 | vue.prototype.$axios = _axios 38 | } 39 | 40 | declare module 'vue/types/vue' { 41 | interface Vue { 42 | $axios: AxiosInstance 43 | } 44 | } 45 | 46 | Vue.use(AxiosPlugin) 47 | 48 | export default AxiosPlugin 49 | -------------------------------------------------------------------------------- /ClientApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp-net-core-vue-starter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "check": "vue-cli-service lint --no-fix" 10 | }, 11 | "dependencies": { 12 | "@mdi/font": "^6.7.96", 13 | "axios": "^0.27.2", 14 | "core-js": "^3.23.2", 15 | "date-fns": "^2.28.0", 16 | "register-service-worker": "^1.7.2", 17 | "vue": "^2.6.14", 18 | "vue-class-component": "^7.2.6", 19 | "vue-property-decorator": "^9.1.2", 20 | "vue-router": "^3.5.4", 21 | "vuetify": "^2.6.6", 22 | "vuex": "^3.6.2", 23 | "vuex-class": "^0.3.2" 24 | }, 25 | "devDependencies": { 26 | "@typescript-eslint/eslint-plugin": "^4.33.0", 27 | "@typescript-eslint/parser": "^4.33.0", 28 | "@vue/cli-plugin-babel": "~4.5.18", 29 | "@vue/cli-plugin-eslint": "~4.5.18", 30 | "@vue/cli-plugin-pwa": "~4.5.18", 31 | "@vue/cli-plugin-router": "~4.5.18", 32 | "@vue/cli-plugin-typescript": "~4.5.18", 33 | "@vue/cli-plugin-vuex": "~4.5.18", 34 | "@vue/cli-service": "~4.5.18", 35 | "@vue/eslint-config-prettier": "^6.0.0", 36 | "@vue/eslint-config-typescript": "^7.0.0", 37 | "eslint": "^6.8.0", 38 | "eslint-plugin-prettier": "^3.4.1", 39 | "eslint-plugin-vue": "^6.2.2", 40 | "node-sass": "^5.0.0", 41 | "prettier": "^2.7.1", 42 | "sass": "^1.53.0", 43 | "sass-loader": "^10.2.1", 44 | "typescript": "^4.7.4", 45 | "vue-cli-plugin-vuetify": "^2.5.1", 46 | "vue-template-compiler": "^2.6.14", 47 | "vuetify-loader": "^1.7.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ClientApp/src/App.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /AspNetCoreVueStarter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | true 6 | Latest 7 | false 8 | ClientApp\ 9 | $(DefaultItemExcludes);$(SpaRoot)node_modules\** 10 | Software Ateliers 11 | Software Ateliers 12 | ASP.NET Core + Vue.js starter project 13 | MIT 2022 Software Ateliers 14 | AspNetCoreVueStarter 15 | AspNetCoreVueStarter 16 | 3.1.2 17 | 3.1.2.0 18 | 3.1.2.0 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | %(DistFiles.Identity) 53 | PreserveNewest 54 | true 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ClientApp/src/views/FetchData.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 91 | -------------------------------------------------------------------------------- /ClientApp/src/views/FetchData-decorator.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 88 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core & Vue Server", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "logging": { 12 | "moduleLoad": false 13 | }, 14 | "env": { 15 | "ASPNETCORE_ENVIRONMENT": "Development", 16 | "VUE_DEV_SERVER_PROGRESS": "true" 17 | }, 18 | "cwd": "${workspaceFolder}", 19 | "preLaunchTask": "build-vue-cli-serve", 20 | // If you have changed target frameworks, make sure to update the program path. 21 | "program": "${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll", 22 | "args": [ 23 | "/mode:attach" 24 | ], 25 | "stopAtEntry": false, 26 | "sourceFileMap": { 27 | "/Views": "${workspaceFolder}/Views" 28 | }, 29 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 30 | "serverReadyAction": { 31 | "action": "startDebugging", 32 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)", 33 | "name": "Chrome Browser" 34 | }, 35 | // this will kill any stray vue-cli processes, as the .NET app can't shut them down when it is killed by the debugger 36 | "postDebugTask": "kill" 37 | }, 38 | { 39 | "name": ".NET Core Web", 40 | "type": "coreclr", 41 | "request": "launch", 42 | "logging": { 43 | "moduleLoad": false 44 | }, 45 | "env": { 46 | "ASPNETCORE_ENVIRONMENT": "Development" 47 | }, 48 | "cwd": "${workspaceFolder}", 49 | "preLaunchTask": "build", 50 | // If you have changed target frameworks, make sure to update the program path. 51 | "program": "${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll", 52 | "args": [ 53 | "/mode:start" 54 | ], 55 | "stopAtEntry": false, 56 | "sourceFileMap": { 57 | "/Views": "${workspaceFolder}/Views" 58 | }, 59 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 60 | "serverReadyAction": { 61 | "action": "startDebugging", 62 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)", 63 | "name": "Chrome Browser" 64 | }, 65 | // this will kill any stray vue-cli processes, as the .NET app can't shut them down when it is killed by the debugger 66 | "postDebugTask": "kill" 67 | }, 68 | { 69 | "name": ".NET Core Attach", 70 | "type": "coreclr", 71 | "request": "attach", 72 | "processId": "${command:pickProcess}" 73 | }, 74 | { 75 | "name": "Chrome Browser", 76 | "type": "chrome", 77 | "request": "launch", 78 | "url": "http://localhost:5000", 79 | "webRoot": "${workspaceFolder}/ClientApp", 80 | "breakOnLoad": true, 81 | "sourceMaps": true, 82 | }, 83 | { 84 | "name": "Firefox Browser", 85 | "type": "firefox", 86 | "request": "launch", 87 | "url": "http://localhost:5000", 88 | "webRoot": "${workspaceFolder}/ClientApp" 89 | } 90 | ] 91 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AspNetCoreVueStarter.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AspNetCoreVueStarter.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AspNetCoreVueStarter.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | }, 41 | { 42 | "label": "kill", 43 | "command": "dotnet", 44 | "type": "process", 45 | "args": [ 46 | "run", 47 | "${workspaceFolder}/bin/Debug/net5.0/AspNetCoreVueStarter.dll", 48 | "/mode:kill" 49 | ], 50 | "presentation": { 51 | "echo": false, 52 | "reveal": "never", 53 | "focus": false, 54 | "panel": "shared", 55 | "showReuseMessage": true, 56 | "clear": false 57 | }, 58 | "problemMatcher": [] 59 | }, 60 | { 61 | "label": "vue-cli-serve", 62 | "command": "npm", 63 | "type": "shell", 64 | "isBackground": true, 65 | "options": { 66 | "cwd": "${workspaceFolder}/ClientApp/", 67 | "env": { 68 | "VUE_DEV_SERVER_PROGRESS": "true" 69 | } 70 | }, 71 | "args": [ 72 | "run", 73 | "serve" 74 | ], 75 | "presentation": { 76 | "echo": false, 77 | "reveal": "always", 78 | "focus": false, 79 | "panel": "dedicated", 80 | "showReuseMessage": false, 81 | "clear": true 82 | }, 83 | "problemMatcher": [ 84 | { 85 | "pattern": [ 86 | { 87 | "regexp": ".", 88 | "file": 1, 89 | "location": 2, 90 | "message": 3 91 | } 92 | ], 93 | "background": { 94 | "activeOnStart": true, 95 | "beginsPattern": ".", 96 | "endsPattern": "App running at", 97 | } 98 | } 99 | ] 100 | }, 101 | { 102 | "label": "build-vue-cli-serve", 103 | "dependsOn": ["build", "vue-cli-serve"], 104 | "dependsOrder": "sequence", 105 | } 106 | 107 | ] 108 | } -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using VueCliMiddleware; 7 | 8 | namespace AspNetCoreVueStarter 9 | { 10 | public class Startup 11 | { 12 | public Startup(IConfiguration configuration) 13 | { 14 | Configuration = configuration; 15 | } 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | // This method gets called by the runtime. Use this method to add services to the container. 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | services.AddControllersWithViews(); 23 | 24 | // Add AddRazorPages if the app uses Razor Pages. 25 | // services.AddRazorPages(); 26 | 27 | // In production, the Vue files will be served from this directory 28 | services.AddSpaStaticFiles(configuration => 29 | { 30 | configuration.RootPath = "ClientApp/dist"; 31 | }); 32 | } 33 | 34 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 35 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 36 | { 37 | 38 | _ = CommandLine.Arguments.TryGetOptions(System.Environment.GetCommandLineArgs(), false, out string mode, out ushort port, out bool https); 39 | 40 | if (env.IsDevelopment()) 41 | { 42 | app.UseDeveloperExceptionPage(); 43 | } 44 | else 45 | { 46 | app.UseExceptionHandler("/Error"); 47 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 48 | app.UseHsts(); 49 | } 50 | 51 | if (https) app.UseHttpsRedirection(); 52 | 53 | app.UseStaticFiles(); 54 | if (!env.IsDevelopment()) 55 | { 56 | app.UseSpaStaticFiles(); 57 | } 58 | 59 | app.UseRouting(); 60 | 61 | app.UseEndpoints(endpoints => 62 | { 63 | endpoints.MapControllerRoute( 64 | name: "default", 65 | pattern: "{controller}/{action=Index}/{id?}"); 66 | 67 | // Add MapRazorPages if the app uses Razor Pages. Since Endpoint Routing includes support for many frameworks, adding Razor Pages is now opt -in. 68 | // endpoints.MapRazorPages(); 69 | }); 70 | 71 | app.UseSpa(spa => 72 | { 73 | // To learn more about options for serving an Angular SPA from ASP.NET Core, 74 | // see https://go.microsoft.com/fwlink/?linkid=864501 75 | 76 | spa.Options.SourcePath = "ClientApp"; 77 | 78 | if (env.IsDevelopment()) 79 | { 80 | 81 | // run npm process with client app 82 | if (mode == "start") { 83 | spa.UseVueCli(npmScript: "serve", port: port, forceKill: true, https: https); 84 | } 85 | 86 | // if you just prefer to proxy requests from client app, use proxy to SPA dev server instead, 87 | // app should be already running before starting a .NET client: 88 | // run npm process with client app 89 | if (mode == "attach") { 90 | spa.UseProxyToSpaDevelopmentServer($"{(https ? "https" : "http")}://localhost:{port}"); // your Vue app port 91 | } 92 | } 93 | }); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ClientApp/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 99 | 100 | 111 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Properties/launchSettings.json 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | build/ 23 | bld/ 24 | bin/ 25 | Bin/ 26 | obj/ 27 | Obj/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | /wwwroot/dist/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Microsoft Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Microsoft Azure Emulator 159 | ecf/ 160 | rcf/ 161 | 162 | # Microsoft Azure ApplicationInsights config file 163 | ApplicationInsights.config 164 | 165 | # Windows Store app package directory 166 | AppPackages/ 167 | BundleArtifacts/ 168 | 169 | # Visual Studio cache files 170 | # files ending in .cache can be ignored 171 | *.[Cc]ache 172 | # but keep track of directories ending in .cache 173 | !*.[Cc]ache/ 174 | 175 | # Others 176 | ClientBin/ 177 | ~$* 178 | *~ 179 | *.dbmdl 180 | *.dbproj.schemaview 181 | *.pfx 182 | *.publishsettings 183 | orleans.codegen.cs 184 | 185 | /node_modules 186 | 187 | # RIA/Silverlight projects 188 | Generated_Code/ 189 | 190 | # Backup & report files from converting an old project file 191 | # to a newer Visual Studio version. Backup files are not needed, 192 | # because we have git ;-) 193 | _UpgradeReport_Files/ 194 | Backup*/ 195 | UpgradeLog*.XML 196 | UpgradeLog*.htm 197 | 198 | # SQL Server files 199 | *.mdf 200 | *.ldf 201 | 202 | # Business Intelligence projects 203 | *.rdl.data 204 | *.bim.layout 205 | *.bim_*.settings 206 | 207 | # Microsoft Fakes 208 | FakesAssemblies/ 209 | 210 | # GhostDoc plugin setting file 211 | *.GhostDoc.xml 212 | 213 | # Node.js Tools for Visual Studio 214 | .ntvs_analysis.dat 215 | 216 | # Visual Studio 6 build log 217 | *.plg 218 | 219 | # Visual Studio 6 workspace options file 220 | *.opt 221 | 222 | # Visual Studio LightSwitch build output 223 | **/*.HTMLClient/GeneratedArtifacts 224 | **/*.DesktopClient/GeneratedArtifacts 225 | **/*.DesktopClient/ModelManifest.xml 226 | **/*.Server/GeneratedArtifacts 227 | **/*.Server/ModelManifest.xml 228 | _Pvt_Extensions 229 | 230 | # Paket dependency manager 231 | .paket/paket.exe 232 | 233 | # FAKE - F# Make 234 | .fake/ 235 | -------------------------------------------------------------------------------- /CommandLine.cs: -------------------------------------------------------------------------------- 1 | // original source: https://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser 2 | 3 | using System; 4 | using System.Collections.Specialized; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace CommandLine 8 | { 9 | public static class Arguments 10 | { 11 | 12 | public static bool TryGetOptions(string[] args, bool inConsole, out string mode, out ushort port, out bool https) 13 | { 14 | var arguments = Parse(args); 15 | var validArgs = true; 16 | 17 | mode = arguments["m"] ?? arguments["mode"] ?? "start"; 18 | https = arguments["http"] == null && arguments["https"] != null; 19 | var portString = arguments["p"] ?? arguments["port"] ?? "8080"; 20 | 21 | if (mode != "start" && mode != "attach" && mode != "kill") 22 | { 23 | if (inConsole) 24 | { 25 | Console.ForegroundColor = ConsoleColor.Red; 26 | Console.WriteLine("Invalid mode; Allowed values are start | attach | kill"); 27 | Console.ResetColor(); 28 | } 29 | validArgs = false; 30 | } 31 | 32 | if (!ushort.TryParse(portString, out port) || port < 80) 33 | { 34 | if (inConsole) 35 | { 36 | Console.ForegroundColor = ConsoleColor.Red; 37 | Console.WriteLine("Invalid port number specified."); 38 | Console.ResetColor(); 39 | } 40 | validArgs = false; 41 | } 42 | 43 | if (arguments["h"] != null || arguments["help"] != null) validArgs = false; 44 | 45 | if (inConsole && !validArgs) 46 | { 47 | Console.WriteLine(); 48 | Console.WriteLine(" Mode Argument Options (Defaults to start)"); 49 | Console.WriteLine(" -m | --mode start -> Start the SPA server and proxy to that."); 50 | Console.WriteLine(" -m | --mode attach -> Attach to existing SPA server"); 51 | Console.WriteLine(" -m | --mode kill -> Shutdown any existing SPA server on the specified port (used after debugging in VS Code)"); 52 | Console.WriteLine(); 53 | Console.WriteLine(" Port Argument (Defaults to 8080)"); 54 | Console.WriteLine(" -p | --port 8080 -> Specify what port to start or attach to, minimum of 80"); 55 | Console.WriteLine(); 56 | Console.WriteLine(" HTTPS (Defaults to false)"); 57 | Console.WriteLine(" -https -> Uses HTTPS"); 58 | Console.WriteLine(); 59 | 60 | } 61 | 62 | return validArgs; 63 | 64 | } 65 | 66 | public static StringDictionary Parse(string[] args) 67 | { 68 | var parameters = new StringDictionary(); 69 | Regex splitter = new Regex(@"^-{1,2}|^/|=|:", 70 | RegexOptions.IgnoreCase | RegexOptions.Compiled); 71 | 72 | Regex remover = new Regex(@"^['""]?(.*?)['""]?$", 73 | RegexOptions.IgnoreCase | RegexOptions.Compiled); 74 | 75 | string parameter = null; 76 | string[] parts; 77 | 78 | // Valid parameters forms: 79 | // {-,/,--}param{ ,=,:}((",')value(",')) 80 | // Examples: 81 | // -param1 value1 --param2 /param3:"Test-:-work" 82 | // /param4=happy -param5 '--=nice=--' 83 | foreach (string txt in args) 84 | { 85 | // Look for new parameters (-,/ or --) and a 86 | // possible enclosed value (=,:) 87 | parts = splitter.Split(txt, 3); 88 | 89 | switch (parts.Length) 90 | { 91 | // Found a value (for the last parameter 92 | // found (space separator)) 93 | case 1: 94 | if (parameter != null) 95 | { 96 | if (!parameters.ContainsKey(parameter)) 97 | { 98 | parts[0] = 99 | remover.Replace(parts[0], "$1"); 100 | 101 | parameters.Add(parameter, parts[0]); 102 | } 103 | parameter = null; 104 | } 105 | // else Error: no parameter waiting for a value (skipped) 106 | break; 107 | 108 | // Found just a parameter 109 | case 2: 110 | // The last parameter is still waiting. 111 | // With no value, set it to true. 112 | if (parameter != null && !parameters.ContainsKey(parameter)) 113 | parameters.Add(parameter, "true"); 114 | 115 | parameter = parts[1]; 116 | break; 117 | 118 | // Parameter with enclosed value 119 | case 3: 120 | // The last parameter is still waiting. 121 | // With no value, set it to true. 122 | if (parameter != null && !parameters.ContainsKey(parameter)) 123 | parameters.Add(parameter, "true"); 124 | 125 | parameter = parts[1]; 126 | 127 | // Remove possible enclosing characters (",') 128 | if (!parameters.ContainsKey(parameter)) 129 | { 130 | parts[2] = remover.Replace(parts[2], "$1"); 131 | parameters.Add(parameter, parts[2]); 132 | } 133 | 134 | parameter = null; 135 | break; 136 | } 137 | } 138 | // In case a parameter is still waiting 139 | if (parameter != null && !parameters.ContainsKey(parameter)) 140 | parameters.Add(parameter, "true"); 141 | 142 | return parameters; 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Vue Starter 2 | 3 | The repository contains an ASP.​NET Core + Vue.js starter template. The template runs on ASP.NET Core 5.0 and is created by Vue CLI 4.0 with a new plugin based architecture allowing developers to interactively scaffold a new project with just a one command. 4 | 5 | Original article how to create the starter template is available [here](https://medium.com/software-ateliers/asp-net-core-vue-template-with-custom-configuration-using-cli-3-0-8288e18ae80b). 6 | 7 | [![Nuget](https://img.shields.io/nuget/v/SoftwareAteliers.AspNetCoreVueStarter.svg?style=flat&color=2196f3)](https://www.nuget.org/packages/SoftwareAteliers.AspNetCoreVueStarter/) 8 | 9 | > For ASP.NET Core 3.1 template use [release v2.6.0](https://github.com/SoftwareAteliers/asp-net-core-vue-starter/releases/tag/2.6.0) 10 | 11 | > For ASP.NET Core 2.2 template use [release v1.1.0](https://github.com/SoftwareAteliers/asp-net-core-vue-starter/releases/tag/1.1.0) 12 | --- 13 | 14 | ## Table of Contents 15 | 16 | * [Features](#features) 17 | * [Used Technology Stack](#used-technology-stack) 18 | * [Prerequisites](#prerequisites) 19 | * [Getting Started](#getting-started) 20 | 21 | ## Features 22 | 23 | * Hot module replacement 24 | * Code-splitting 25 | * Tree-shaking 26 | * ES2017 transpilation 27 | * Long term caching and so on 28 | 29 | ## Used Technology Stack 30 | 31 | **ASP.NET Core 5.0:** 32 | 33 | * Web.API 34 | * Vue CLI and JavaScript Services middlewares to integrate with client app 35 | 36 | **Vue.js with CLI 4.0 supporting optional integrations:** 37 | 38 | * TypeScript 39 | * Progressive Web App 40 | * Vue Router & Vuex (State Store) 41 | * Linting, unit testing, E2E testing 42 | * 3rd party component frameworks (Vuetify, Vue Bootstrap etc.) 43 | * publish your personal/enterprise plugin and so on... 44 | 45 | *For a full feature list, I suggest you to read the [official CLI release statement](https://medium.com/the-vue-point/vue-cli-3-0-is-here-c42bebe28fbb) by Evan You.* 46 | 47 | ## Prerequisites 48 | 49 | * [.NET Core](https://www.microsoft.com/net/download/windows) >= 5.0 50 | * [NodeJS](https://nodejs.org/) >= 8.9 51 | * [Vue CLI](https://cli.vuejs.org/) >= 4.0 52 | * Your favourite editor (I prefer [VS Code](https://code.visualstudio.com/)), or VS 2017/19 53 | 54 | --- 55 | 56 | ## Getting started 57 | 58 | There are two ways how to set up the project: one for people who want to create their own template and choose custom integrations and the other for developers who want to start with no configuration. 59 | 60 | ## Clone the starter with default configuration 61 | 62 | * Clone this repository `git clone https://github.com/SoftwareAteliers/asp-net-core-vue-starter` 63 | 64 | or you can use .NET Core CLI templates: 65 | 66 | * Install the template from NuGet repository: `dotnet new -i SoftwareAteliers.AspNetCoreVueStarter` 67 | 68 | * Initialize the project: `dotnet new vue -o MyProject` 69 | 70 | ## (Optional) Scaffold Vue.js app with custom configuration 71 | 72 | If you prefer to overwrite default Vue client app with custom settings, take the following steps: 73 | 74 | * **Remove all the contents** of the folder /ClientApp 75 | * Create a new Vue project by using Vue CLI: `vue create client-app` OR by using CLI graphical interface running `vue ui` 76 | 77 | > Unfortunately Vue CLI does not allow us to set a project name by C# standards using Upper Camel Case (Pascal Case) naming convention, so let's initiate app inside of client-app folder and then move the content to ClientApp. 78 | 79 | * **Move all the contents from the new folder /client-app to /ClientApp.** 80 | 81 | Now application is ready to run. 82 | 83 | ## Run the application 84 | 85 | You have three choices when it comes to how you prefer to run the app. You can either use the command line or the build-in run command. 86 | 87 | ### 1. Using the command line 88 | 89 | * Run the .NET application using `dotnet run` 90 | 91 | ### 2. Using the built-in run command 92 | 93 | * Run the application in VSCode or Visual Studio 2017 by hitting `F5` 94 | 95 | > It will take some time during the first run to download all client side dependencies. 96 | 97 | Browse to [http://localhost:5000](http://localhost:5000) for ASP.​NET Core + Vue app or browse to [http://localhost:8080](http://localhost:8080) for Vue app only. 98 | 99 | ![Application screenshot](./screenshot.png) 100 | 101 | ## Publish the application 102 | 103 | ### 1. Folder output 104 | 105 | * Run the .NET publish command using Release configuration: `dotnet publish -c Release` 106 | 107 | or 108 | 109 | * Follow the Publish wizard in Visual Studio selecting Folder profile. 110 | 111 | ### 2. Docker output 112 | 113 | * Run the following command in a cmd window to build the docker image: 114 | `docker build -t .` 115 | 116 | > ATTENTION! Do not miss the final dot to build the current directory 117 | 118 | * Run the application in a cmd window by this command: 119 | `docker run -d -p 5000:80 ` 120 | 121 | ## View your application running 122 | 123 | 124 | ## Recommended plugin for debugging Vue 125 | 126 | * Get Chrome DevTools for Vue.js [here](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) 127 | 128 | --- 129 | 130 | ## Issues and Contribution 131 | 132 | Want to file a bug, contribute some code, or improve documentation? Excellent! Please make sure to check existing issues before opening a new one. 133 | 134 | --- 135 | 136 | ## Contributors 137 | 138 | Special thanks to everyone who helped and contributed to this project! 139 | 140 | * [@jdebarochez](https://github.com/jdebarochez) 141 | * [@arisliang](https://github.com/arisliang) 142 | * [@dotnetshadow](https://github.com/dotnetshadow) 143 | * [@NickStees](https://github.com/NickStees) 144 | * [@wallyjue](https://github.com/wallyjue) 145 | * [@Ibrahim-Islam](https://github.com/Ibrahim-Islam) 146 | * [@bickycheese](https://github.com/bickycheese) 147 | * [@nickyg91](https://github.com/nickyg91) 148 | * [@glebov21](https://github.com/glebov21) 149 | * [@TobbenTM](https://github.com/TobbenTM) 150 | * [@JohnCampionJr](https://github.com/JohnCampionJr) 151 | 152 | --- 153 | 154 | ## License 155 | 156 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://mit-license.org/) 157 | 158 | Copyright © 2018 - 2022 [Software Ateliers](https://github.com/SoftwareAteliers) 159 | 160 | ## Where to find me 161 | 162 | Medium: [Software Ateliers](https://medium.com/software-ateliers) | Twitter: [@SAteliers](https://twitter.com/SAteliers) --------------------------------------------------------------------------------