diff --git a/.editorconfig b/.editorconfig index 869ff6c553..57292716f7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# @AngularClass +# tipe.io # http://editorconfig.org root = true diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 74b102c5fe..7e4625347f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ ## Read and contribute to the Wiki -Make sure you read the [Wiki](https://github.com/AngularClass/angular-starter/wiki). +Make sure you read the [Wiki](https://github.com/gdi2290/angular-starter/wiki). ## Submitting Pull Requests diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..7691371586 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [PatrickJS] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1b4e0765d1..ba1a09ad84 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,3 @@ -**Note: for support questions, please use one of these channels:** [Chat: AngularClass.slack](http://angularclass.com/member-join/) or [Twitter: @AngularClass](https://twitter.com/AngularClass) - * **I'm submitting a ...** [ ] bug report [ ] feature request @@ -28,7 +26,7 @@ https://plnkr.co or similar (you can use this template as a starting point: http * **Please tell us about your environment:** -- Angular version: 2.0.0-beta.X +- Angular version: 4.x.x - Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] diff --git a/.gitignore b/.gitignore index 5525b5026f..1a2a9656e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# @AngularClass +# tipe.io # Logs logs @@ -15,6 +15,9 @@ lib-cov # Coverage directory used by tools like istanbul coverage +# SonarQube sonar-scanner temp directory +.scannerwork + # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt @@ -39,6 +42,9 @@ npm-debug.log # Coverage # /coverage/ +# Generic Unit Sonar Reports # +/reports/ + # Typing # /src/typings/tsd/ /typings/ @@ -56,9 +62,13 @@ npm-debug.log .webpack.json /compiled/ dll/ +temp +tmp +webpack-cache # Doc # /doc/ +/documentation/ # IDE # .idea/ diff --git a/.travis.yml b/.travis.yml index 5e1e37f41a..49c6a52523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,16 +7,12 @@ cache: - node_modules addons: - apt: - sources: - - google-chrome - packages: - - google-chrome-stable + chrome: stable node_js: - "6" - "7" - - "node" + - "8" matrix: fast_finish: true @@ -30,15 +26,5 @@ before_install: install: - npm install -after_install: - - npm rebuild node-sass - - ./node_modules/protractor/bin/webdriver-manager update - -before_script: - - npm rebuild node-sass - - export DISPLAY=:99.0 - - sh -e /etc/init.d/xvfb start - - sleep 3 - script: - npm run ci:travis diff --git a/Dockerfile b/Dockerfile index e04136ab21..f74a52d27e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,45 +1,39 @@ -# Usage (given build times depend on machine): +# Usage: # -# Build SMALL image (no cache; ~20MB, time for build=rebuild = ~360s): -# docker build --squash="true" -t angular-starter . -# -# Build FAST (rebuild) image (cache; >280MB, build time ~360s, rebuild time ~80s): +# Build image: # docker build -t angular-starter . # -# Clean (remove intermidiet images): -# docker rmi -f $(docker images -f "dangling=true" -q) -# # Run image (on localhost:8080): -# docker run --name angular-starter -p 8080:80 angular-starter & +# docker run --name angular-starter -p 8080:80 angular-starter # # Run image as virtual host (read more: https://github.com/jwilder/nginx-proxy): -# docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter & - -FROM nginx:1.13.0-alpine - -# install console and node -RUN apk add --no-cache bash=4.3.46-r5 &&\ - apk add --no-cache openssl=1.0.2k-r0 &&\ - apk add --no-cache nodejs - -# install npm ( in separate dir due to docker cache) -ADD package.json /tmp/npm_inst/package.json -RUN cd /tmp/npm_inst &&\ - npm install &&\ - mkdir -p /tmp/app &&\ - mv /tmp/npm_inst/node_modules /tmp/app/ - -# build and publish application -ADD . /tmp/app -RUN cd /tmp/app &&\ - npm run build:aot &&\ - mv ./dist/* /usr/share/nginx/html/ - -# clean -RUN rm -Rf /tmp/npm_inst &&\ - rm -Rf /tmp/app &&\ - rm -Rf /root/.npm &&\ - apk del nodejs - -# this is for virtual host purposes -EXPOSE 80 +# docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter + +# Stage 1, based on Node.js, to build and compile Angular + +FROM node:8.9.4-alpine as builder + +COPY package.json ./ + +## Storing node modules on a separate layer will prevent unnecessary npm installs at each build +RUN npm i && mkdir /ng-app && mv ./node_modules ./ng-app + +WORKDIR /ng-app + +COPY . . + +RUN npm run build:aot:prod + +# Stage 2, based on Nginx, to have only the compiled app, ready for production with Nginx + +FROM nginx:1.13.9-alpine + +COPY ./config/nginx-custom.conf /etc/nginx/conf.d/default.conf + +## Remove default nginx website +RUN rm -rf /usr/share/nginx/html/* + +## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder +COPY --from=builder /ng-app/dist /usr/share/nginx/html + +CMD ["nginx", "-g", "daemon off;"] diff --git a/Dockerfile-dev b/Dockerfile-dev new file mode 100644 index 0000000000..051ca9d399 --- /dev/null +++ b/Dockerfile-dev @@ -0,0 +1,21 @@ +# Usage: +# +# Build image: +# docker build -t angular-starter . +# +# Run image (on localhost:8080): +# docker run --name angular-starter -p 8080:80 angular-starter +# +# Run image as virtual host (read more: https://github.com/jwilder/nginx-proxy): +# docker run -e VIRTUAL_HOST=angular-starter.your-domain.com --name angular-starter angular-starter + +FROM node:8.9.4-alpine as builder + +COPY package.json ./ + +## Storing node modules on a separate layer will prevent unnecessary npm installs at each build +RUN npm i && mkdir /ng-app && mv ./node_modules ./ng-app + +WORKDIR /ng-app + +COPY . . diff --git a/LICENSE b/LICENSE index b71a4a7446..3c39f2bade 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 AngularClass LLC +Copyright (c) 2015-2018 PatrickJS, Tipe Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index 1fba0996ac..c14c84b6ad 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,56 @@ -

- - OneSpeed - -

- -___ +# Please use the Angular CLI if you want an angular app [![taylor swift](https://img.shields.io/badge/secured%20by-taylor%20swift-brightgreen.svg)](https://twitter.com/SwiftOnSecurity) [![volkswagen status](https://auchenberg.github.io/volkswagen/volkswargen_ci.svg?v=1)](https://github.com/auchenberg/volkswagen) -[![Build Status](https://travis-ci.org/AngularClass/angular2-webpack-starter.svg?branch=master)](https://travis-ci.org/AngularClass/angular2-webpack-starter) -[![GitHub version](https://badge.fury.io/gh/angularclass%2Fangular2-webpack-starter.svg)](https://badge.fury.io/gh/angularclass%2Fangular2-webpack-starter) -[![Dependency Status](https://david-dm.org/angularclass/angular2-webpack-starter.svg)](https://david-dm.org/angularclass/angular2-webpack-starter) -[![Stack Share](http://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](http://stackshare.io/angularclass/angular-2-webpack-starter) +[![Build Status](https://travis-ci.org/gdi2290/angular-starter.svg?branch=master)](https://travis-ci.org/gdi2290/angular-starter) +[![GitHub version](https://badge.fury.io/gh/gdi2290%2Fangular-starter.svg)](https://badge.fury.io/gh/gdi2290%2Fangular-starter) +[![Dependency Status](https://david-dm.org/gdi2290/angular-starter.svg)](https://david-dm.org/gdi2290/angular-starter) +[![Stack Share](http://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](http://stackshare.io/gdi2290/angular-starter)

- - Angular Starter with Webpack by OneSpeed + + Angular Starter

-# Angular4 Webpack Starter [![Join Slack](https://img.shields.io/badge/slack-join-brightgreen.svg)](https://angularclass.com/slack-join) [![Join the chat at https://gitter.im/angularclass/angular2-webpack-starter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angularclass/angular2-webpack-starter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Angular Webpack Starter [![Join the chat at https://gitter.im/angularclass/angular2-webpack-starter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angularclass/angular2-webpack-starter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -> An Angular starter kit featuring [Angular 4](https://angular.io), [Ahead of Time Compile](https://angular.io/docs/ts/latest/cookbook/aot-compiler.html), [Router](https://angular.io/docs/ts/latest/guide/router.html), [Forms](https://angular.io/docs/ts/latest/guide/forms.html), +> An Angular starter kit featuring [Angular 6](https://angular.io), [Ahead of Time Compile](https://angular.io/docs/ts/latest/cookbook/aot-compiler.html), [Router](https://angular.io/docs/ts/latest/guide/router.html), [Forms](https://angular.io/docs/ts/latest/guide/forms.html), [Http](https://angular.io/docs/ts/latest/guide/server-communication.html), [Services](https://gist.github.com/gdi2290/634101fec1671ee12b3e#_follow_@AngularClass_on_twitter), -[Tests](https://angular.io/docs/ts/latest/guide/testing.html), [E2E](https://angular.github.io/protractor/#/faq#what-s-the-difference-between-karma-and-protractor-when-do-i-use-which-)), [Karma](https://karma-runner.github.io/), [Protractor](https://angular.github.io/protractor/), [Jasmine](https://github.com/jasmine/jasmine), [Istanbul](https://github.com/gotwarlost/istanbul), [TypeScript](http://www.typescriptlang.org/), [@types](https://www.npmjs.com/~types), [TsLint](http://palantir.github.io/tslint/), [Codelyzer](https://github.com/mgechev/codelyzer), [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html), and [Webpack 2](http://webpack.github.io/) by [AngularClass](https://angularclass.com). +[Tests](https://angular.io/docs/ts/latest/guide/testing.html), [E2E](https://angular.github.io/protractor/#/faq#what-s-the-difference-between-karma-and-protractor-when-do-i-use-which-)), [Karma](https://karma-runner.github.io/), [Protractor](https://angular.github.io/protractor/), [Jasmine](https://github.com/jasmine/jasmine), [Istanbul](https://github.com/gotwarlost/istanbul), [TypeScript](http://www.typescriptlang.org/), [@types](https://www.npmjs.com/~types), [TsLint](http://palantir.github.io/tslint/), [Codelyzer](https://github.com/mgechev/codelyzer), [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html), and [Webpack](http://webpack.github.io/). -> If you're looking for Angular 1.x please use [NG6-starter](https://github.com/angularclass/NG6-starter) +> If you're looking for Angular 1.x please use [NG6-starter](https://github.com/gdi2290/NG6-starter) > If you're looking to learn about Webpack and ES6 Build Tools check out [ES6-build-tools](https://github.com/AngularClass/ES6-build-tools) > If you're looking to learn TypeScript see [TypeStrong/learn-typescript](https://github.com/TypeStrong/learn-typescript) -> If you're looking for something easier to get started with then see the angular-seed that I also maintain [AngularClass/angular-seed](https://github.com/AngularClass/angular-seed) +> If you're looking for something easier to get started with then see the angular-seed that I also maintain [gdi2290/angular-seed](https://github.com/gdi2290/angular-seed) -This seed repo serves as an Angular starter for anyone looking to get up and running with Angular and TypeScript fast. Using a [Webpack 3](https://webpack.js.org) for building our files and assisting with boilerplate. We're also using Protractor for our end-to-end story and Karma for our unit tests. +This seed repo serves as an Angular starter for anyone looking to get up and running with Angular and TypeScript fast. Using a [Webpack 4](https://webpack.js.org) for building our files and assisting with boilerplate. We're also using Protractor for our end-to-end story and Karma for our unit tests. * Best practices in file and application organization for Angular. * Ready to go build system using Webpack for working with TypeScript. * Angular examples that are ready to go when experimenting with Angular. * A great Angular seed repo for anyone who wants to start their project. * Ahead of Time (AoT) compile for rapid page loads of your production builds. * Tree shaking to automatically remove unused code from your production bundle. -* [Webpack DLLs](https://robertknight.github.io/posts/webpack-dll-plugins/) dramatically speed your development builds. * Testing Angular code with Jasmine and Karma. * Coverage with Istanbul and Karma -* End-to-end Angular code using Protractor. +* End-to-end Angular app testing using Protractor. * Type manager with @types -* Hot Module Replacement with Webpack and [@angularclass/hmr](https://github.com/angularclass/angular-hmr) and [@angularclass/hmr-loader](https://github.com/angularclass/angular-hmr-loader) -* Angular 4 support via changing package.json and any future Angular versions +* Hot Module Replacement with Webpack and [@gdi2290/hmr](https://github.com/gdi2290/angular-hmr) and [@gdi2290/hmr-loader](https://github.com/PatrickJS/angular-hmr-loader) ### Quick start -**Make sure you have Node version >= 6.0 and NPM >= 3** +**Make sure you have Node version >= 8.0 and (NPM >= 5 or [Yarn](https://yarnpkg.com) )** > Clone/Download the repo then edit `app.component.ts` inside [`/src/app/app.component.ts`](/src/app/app.component.ts) ```bash # clone our repo # --depth 1 removes all but one .git commit history -git clone --depth 1 https://github.com/AngularClass/angular-starter.git +git clone --depth 1 https://github.com/gdi2290/angular-starter.git # change directory to our repo cd angular-starter -# WINDOWS only. In terminal as administrator -npm install -g node-pre-gyp - # install the repo with npm npm install @@ -99,10 +88,16 @@ We use the component approach in our starter. This is the new standard for devel ``` angular-starter/ ├──config/ * our configuration + | ├──build-utils.js * common config and shared functions for prod and dev + | ├──config.common.json * config for both environments prod and dev such title and description of index.html + | ├──config.dev.json * config for development environment + | ├──config.prod.json * config for production environment + │ │ (note: you can load your own config file, just set the evn ANGULAR_CONF_FILE with the path of your own file) | ├──helpers.js * helper functions for our configuration files | ├──spec-bundle.js * ignore this magic that sets up our Angular testing environment | ├──karma.conf.js * karma config for our unit tests | ├──protractor.conf.js * protractor config for our end-to-end tests + │ ├──webpack.common.js * common tasks for webpack build process shared for dev and prod │ ├──webpack.dev.js * our development webpack config │ ├──webpack.prod.js * our production webpack config │ └──webpack.test.js * our testing webpack config @@ -139,9 +134,9 @@ angular-starter/ ## Dependencies What you need to run this app: * `node` and `npm` (`brew install node`) -* Ensure you're running the latest versions Node `v6.x.x`+ (or `v7.x.x`) and NPM `3.x.x`+ +* Ensure you're running the latest versions Node `v8.x.x`+ (or `v9.x.x`) and NPM `5.x.x`+ -> If you have `nvm` installed, which is highly recommended (`brew install nvm`) you can do a `nvm install --lts && nvm use` in `$` to run with the latest Node LTS. You can also have this `zsh` done for you [automatically](https://github.com/creationix/nvm#calling-nvm-use-automatically-in-a-directory-with-a-nvmrc-file) +> If you have `nvm` installed, which is highly recommended (`brew install nvm`) you can do a `nvm install --lts && nvm use` in `$` to run with the latest Node LTS. You can also have this `zsh` done for you [automatically](https://github.com/creationix/nvm#calling-nvm-use-automatically-in-a-directory-with-a-nvmrc-file) Once you have those, you should install these globals with `npm install --global`: * `webpack` (`npm install --global webpack`) @@ -149,6 +144,7 @@ Once you have those, you should install these globals with `npm install --global * `karma` (`npm install --global karma-cli`) * `protractor` (`npm install --global protractor`) * `typescript` (`npm install --global typescript`) +* `tslint` (`npm install --global tslint@4.5.1`) ## Installing * `fork` this repo @@ -170,6 +166,7 @@ npm run server:prod ``` ## Other commands +## the following commands with npm can be used with yarn as well ### build files ```bash @@ -238,13 +235,15 @@ The following are some things that will make AoT compile fail. - Don’t use functions in your providers, routes or declarations, export a function and then reference that function name - @Inputs, @Outputs, View or Content Child(ren), Hostbindings, and any field you use from the template or annotate for Angular should be public +For more detailed guide on AoT's Do's and Don'ts refer to https://github.com/rangle/angular-2-aot-sandbox + # External Stylesheets Any stylesheets (Sass or CSS) placed in the `src/styles` directory and imported into your project will automatically be compiled into an external `.css` and embedded in your production builds. For example to use Bootstrap as an external stylesheet: 1) Create a `styles.scss` file (name doesn't matter) in the `src/styles` directory. -2) `npm install` the version of Boostrap you want. -3) In `styles.scss` add `@import 'bootstrap/scss/bootstrap.scss';` +2) `npm install` the version of Bootstrap you want. +3) In `styles.scss` add `@import '~bootstrap/scss/bootstrap.scss';` 4) In `src/app/app.module.ts` add underneath the other import statements: `import '../styles/styles.scss';` # Contributing @@ -254,7 +253,7 @@ You can include more examples as components but they must introduce a new concep > To take full advantage of TypeScript with autocomplete you would have to install it globally and use an editor with the correct TypeScript plugins. ## Use latest TypeScript compiler -TypeScript 2.1.x includes everything you need. Make sure to upgrade, even if you installed TypeScript previously. +TypeScript 2.7.x includes everything you need. Make sure to upgrade, even if you installed TypeScript previously. ``` npm install --global typescript @@ -264,23 +263,23 @@ npm install --global typescript We have good experience using these editors: * [Visual Studio Code](https://code.visualstudio.com/) -* [Webstorm 10](https://www.jetbrains.com/webstorm/download/) +* [Webstorm 2018.1](https://www.jetbrains.com/webstorm/download/) * [Atom](https://atom.io/) with [TypeScript plugin](https://atom.io/packages/atom-typescript) * [Sublime Text](http://www.sublimetext.com/3) with [Typescript-Sublime-Plugin](https://github.com/Microsoft/Typescript-Sublime-plugin#installation) ### Visual Studio Code + Debugger for Chrome -> Install [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) and see docs for instructions to launch Chrome +> Install [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) and see docs for instructions to launch Chrome The included `.vscode` automatically connects to the webpack development server on port `3000`. # Types > When you include a module that doesn't include Type Definitions inside of the module you can include external Type Definitions with @types -i.e, to have youtube api support, run this command in terminal: +i.e, to have youtube api support, run this command in terminal: ```shell npm i @types/youtube @types/gapi @types/gapi.youtube -``` -In some cases where your code editor doesn't support Typescript 2 yet or these types weren't listed in ```tsconfig.json```, add these to **"src/custom-typings.d.ts"** to make peace with the compile check: +``` +In some cases where your code editor doesn't support Typescript 2 yet or these types weren't listed in ```tsconfig.json```, add these to **"src/custom-typings.d.ts"** to make peace with the compile check: ```es6 import '@types/gapi.youtube'; import '@types/gapi'; @@ -323,7 +322,7 @@ import * as _ from 'lodash'; # Frequently asked questions * What's the current browser support for Angular? - * Please view the updated list of [browser support for Angular 2](https://github.com/angularclass/awesome-angular2#current-browser-support-for-angular-2) + * Please view the updated list of [browser support for Angular](https://github.com/gdi2290/awesome-angular#current-browser-support-for-angular) * Why is my service, aka provider, is not injecting parameter correctly? * Please use `@Injectable()` for your service for typescript to correctly attach the metadata (this is a TypeScript problem) * Where do I write my tests? @@ -331,19 +330,19 @@ import * as _ from 'lodash'; * How do I start the app when I get `EACCES` and `EADDRINUSE` errors? * The `EADDRINUSE` error means the port `3000` is currently being used and `EACCES` is lack of permission for webpack to build files to `./dist/` * How to use `sass` for css? - * * `loaders: ['raw-loader','sass-loader']` and `@Component({ styleUrls: ['./filename.scss'] })` see Wiki page [How to include SCSS in components](https://github.com/AngularClass/angular-starter/wiki/How-to-include-SCSS-in-components), or issue [#136](https://github.com/AngularClass/angular-starter/issues/136) for more information. + * * `loaders: ['raw-loader','sass-loader']` and `@Component({ styleUrls: ['./filename.scss'] })` see Wiki page [How to include SCSS in components](https://github.com/gdi2290/angular-starter/wiki/How-to-include-SCSS-in-components), or issue [#136](https://github.com/gdi2290/angular-starter/issues/136) for more information. * How do I test a Service? - * See issue [#130](https://github.com/AngularClass/angular-starter/issues/130#issuecomment-158872648) + * See issue [#130](https://github.com/gdi2290/angular-starter/issues/130#issuecomment-158872648) * How do I add `vscode-chrome-debug` support? - * The VS Code chrome debug extension support can be done via `launch.json` see issue [#144](https://github.com/AngularClass/angular-starter/issues/144#issuecomment-164063790) + * The VS Code chrome debug extension support can be done via `launch.json` see issue [#144](https://github.com/gdi2290/angular-starter/issues/144#issuecomment-164063790) * How do I make the repo work in a virtual machine? - * You need to use `0.0.0.0` so revert these changes [#205](https://github.com/AngularClass/angular-starter/pull/205/files) + * You need to use `0.0.0.0` so revert these changes [#205](https://github.com/gdi2290/angular-starter/pull/205/files) * What are the naming conventions for Angular? - * please see issue [#185](https://github.com/AngularClass/angular-starter/issues/185) and PR [196](https://github.com/AngularClass/angular-starter/pull/196) + * please see issue [#185](https://github.com/gdi2290/angular-starter/issues/185) and PR [196](https://github.com/gdi2290/angular-starter/pull/196) * How do I include bootstrap or jQuery? - * please see issue [#215](https://github.com/AngularClass/angular-starter/issues/215) and [#214](https://github.com/AngularClass/angular-starter/issues/214#event-511768416) + * please see issue [#215](https://github.com/gdi2290/angular-starter/issues/215) and [#214](https://github.com/gdi2290/angular-starter/issues/214#event-511768416) * How do I async load a component? - * see wiki [How-do-I-async-load-a-component-with-AsyncRoute](https://github.com/AngularClass/angular-starter/wiki/How-do-I-async-load-a-component-with-AsyncRoute) + * see wiki [How-do-I-async-load-a-component-with-AsyncRoute](https://github.com/gdi2290/angular-starter/wiki/How-do-I-async-load-a-component-with-AsyncRoute) * Error: Cannot find module 'tapable' * Remove `node_modules/` and run `npm cache clean` then `npm install` * How do I turn on Hot Module Replacement @@ -355,24 +354,23 @@ import * as _ from 'lodash'; * If you're in China * check out https://github.com/cnpm/cnpm * node-pre-gyp ERR in npm install (Windows) - * install Python x86 version between 2.5 and 3.0 on windows see issue [#626](https://github.com/AngularClass/angular-starter/issues/626) + * often happens when you're behind proxy and proxy wasn't configured in the npm as it tries to download binary package from the github and if it fails to do so, it will try to compile node-sass from the source codes + * install Python3 x86 * `Error:Error: Parse tsconfig error [{"messageText":"Unknown compiler option 'lib'.","category":1,"code":5023},{"messageText":"Unknown compiler option 'strictNullChecks'.","category":1,"code":5023},{"messageText":"Unknown compiler option 'baseUrl'.","category":1,"code":5023},{"messageText":"Unknown compiler option 'paths'.","category":1,"code":5023},{"messageText":"Unknown compiler option 'types'.","category":1,"code":5023}]` - * remove `node_modules/typescript` and run `npm install typescript@beta`. This repo now uses ts 2.0 + * remove `node_modules/typescript` and run `npm install typescript@beta`. This repo now uses ts 2.0 * "There are multiple modules with names that only differ in casing" - * change `c:\[path to angular-starter]` to `C:\[path to angular-starter]` see [926#issuecomment-245223547](https://github.com/AngularClass/angular-starter/issues/926#issuecomment-245223547) + * change `c:\[path to angular-starter]` to `C:\[path to angular-starter]` see [926#issuecomment-245223547](https://github.com/gdi2290/angular-starter/issues/926#issuecomment-245223547) # Support, Questions, or Feedback > Contact us anytime for anything about this repo or Angular -* [Chat: AngularClass.slack](http://angularclass.com/member-join/) -* [Twitter: @AngularClass](https://twitter.com/AngularClass) -* [Gitter: AngularClass/angular2-webpack-starter](https://gitter.im/angularclass/angular2-webpack-starter) +`@PatrickJS__` on twitter # Deployment ## Docker -To run project you only need host machine with **operating system** with installed **git** (to clone this repo) +To run project you only need host machine with **operating system** with installed **git** (to clone this repo) and [docker](https://www.docker.com/) and thats all - any other software is not needed (other software like node.js etc. will be automatically downloaded and installed inside docker container during build step based on dockerfile). @@ -393,7 +391,7 @@ sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial ma sudo apt-get update apt-cache policy docker-engine sudo apt-get install -y docker-engine -sudo systemctl status docker # test: shoud be ‘active’ +sudo systemctl status docker # test: should be ‘active’ ``` And add your user to docker group (to avoid `sudo` before using `docker` command in future): ``` @@ -403,22 +401,16 @@ and logout and login again. ### Build image -Because *node.js* is big memory consumer you need 1-2GB RAM or virtual memory to build docker image +Because *node.js* is big memory consumer you need 1-2GB RAM or virtual memory to build docker image (it was successfully tested on machine with 512MB RAM + 2GB virtual memory - building process take 7min) -Go to main project folder. To build big (~280MB) image which has cached data and is able to **FAST** rebuild -(this is good for testing or staging environment) type: +Go to main project folder. To build image type: `docker build -t angular-starter .` -To build **SMALL** (~20MB) image without cache (so each rebuild will take the same amount of time as first build) -(this is good for production environment) type: - -`docker build --squash="true" -t angular-starter .` - -The **angular-starter** name used in above commands is only example image name. +The **angular-starter** name used in above commands is only example image name. To remove intermediate images created by docker on build process, type: - + `docker rmi -f $(docker images -f "dangling=true" -q)` ### Run image @@ -429,24 +421,31 @@ To run created docker image on [localhost:8080](localhost:8080) type (parameter And that's all, you can open browser and go to [localhost:8080](localhost:8080). +### Build and Run image using docker-compose + +To create and run docker image on [localhost:8080](localhost:8080) as part of large project you may use **docker-compose**. Type + +`docker-compose up` + +And that's all, you can open browser and go to [localhost:8080](localhost:8080). + + ### Run image on sub-domain -If you want to run image as virtual-host on sub-domain you must setup [proxy](https://github.com/jwilder/nginx-proxy) -. You should install proxy and set sub-domain in this way: - +If you want to run image as virtual-host on sub-domain you must setup [proxy](https://github.com/jwilder/nginx-proxy). You should install proxy and set sub-domain in this way: + ``` - docker pull jwilder/nginx-proxy:alpine docker run -d -p 80:80 --name nginx-proxy -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy:alpine ``` - + And in your `/etc/hosts` file (linux) add line: `127.0.0.1 angular-starter.your-domain.com` or in yor hosting add - folowing DNS record (wildchar `*` is handy because when you add new sub-domain in future, you don't need to touch/add any DNS record) - + following DNS record (wildchar `*` is handy because when you add new sub-domain in future, you don't need to touch/add any DNS record) + ``` - Type: CNAME + Type: CNAME Hostname: *.your-domain.com Direct to: your-domain.com - TTL(sec): 43200 + TTL(sec): 43200 ``` And now you are ready to run image on subdomain by: @@ -466,16 +465,41 @@ starter kit in production on [Netlify](https://www.netlify.com/): [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/AngularClass/angular-starter) +### Optional Integration with SonarQube (for continous code quality) +Assuming you have SonarQube 5.5.6 (LTS) installed +* Setup SonarQube with the [Sonar Typescript plugin](https://github.com/Pablissimo/SonarTsPlugin#installation) and the Generic Test Coverage plugin https://docs.sonarqube.org/display/PLUG/Generic+Test+Coverage +* Install sonar-scanner globally +```bash +npm install --global sonar-scanner +``` +* Install the [Karma plugin for sonarqube](https://www.npmjs.com/package/karma-sonarqube-unit-reporter) as a dev dependency +```bash +npm install karma-sonarqube-unit-reporter --save-dev +``` +* Sonar Host URL configuration: +Update [`sonar-project.properties`](sonar-project.properties) file for the property `sonar.host.url` to point to your SonarQube server. By default this assumes that the SonarQube server is running locally using the default port +``` +sonar.host.url= +``` +* Run the unit tests with sonar reporter enabled +```bash +npm run test:sonar +``` +* The test results collected in the results folder in the sonar compatible format +* Push results to SonarCube +```bash +sonar-scanner +``` +* If working with SonarQube 6.x it supports [Generic Test Data](https://docs.sonarqube.org/display/SONAR/Generic+Test+Data) +* Modify the [karma.conf.js](config/karma.config.js) to set the appropriate version of the sonarQube +```es6 +sonarQubeUnitReporter: { + sonarQubeVersion: '6.x', +} +``` ___ -enjoy — **AngularClass** a **OneSpeed** company - -

- -[![OneSpeed](https://user-images.githubusercontent.com/1016365/28739734-462f5210-73b2-11e7-92f7-2aabe05cfefa.png "OneSpeed")](https://onespeed.io) -## [OneSpeed](https://onespeed.io) -> Our experience is vast with OneSpeed successfully creating and delivering solutions for companies like Bloomberg, American Airlines, and IBM. -> We will tailor our solutions to fit your needs. Web App Development Services, Mobile App Services, Corporate Training contact us at Patrick@OneSpeed.io +enjoy — [**PatrickJS**](https://patrickjs.com) ___ diff --git a/config/build-utils.js b/config/build-utils.js new file mode 100644 index 0000000000..6e94e92ff9 --- /dev/null +++ b/config/build-utils.js @@ -0,0 +1,131 @@ +const ts = require('typescript'); +const path = require('path'); +const fs = require('fs'); +const helpers = require('./helpers'); + +const APP_COMMON_CONFIG = require('./config.common.json'); + +const DEFAULT_METADATA = { + title: APP_COMMON_CONFIG.title, + description: APP_COMMON_CONFIG.description, + baseUrl: '/', + isDevServer: helpers.isWebpackDevServer(), + HMR: helpers.hasProcessFlag('hot'), + AOT: process.env.BUILD_AOT || helpers.hasNpmFlag('aot'), + E2E: !!process.env.BUILD_E2E, + WATCH: helpers.hasProcessFlag('watch'), + tsConfigPath: 'tsconfig.webpack.json', + + /** + * This suffix is added to the environment.ts file, if not set the default environment file is loaded (development) + * To disable environment files set this to null + */ + envFileSuffix: '' +}; + +function supportES2015(tsConfigPath) { + if (!supportES2015.hasOwnProperty('supportES2015')) { + const tsTarget = readTsConfig(tsConfigPath).options.target; + supportES2015['supportES2015'] = tsTarget !== ts.ScriptTarget.ES3 && tsTarget !== ts.ScriptTarget.ES5; + } + return supportES2015['supportES2015']; +} + +function readTsConfig(tsConfigPath) { + const configResult = ts.readConfigFile(tsConfigPath, ts.sys.readFile); + return ts.parseJsonConfigFileContent(configResult.config, ts.sys, + path.dirname(tsConfigPath), undefined, tsConfigPath); +} + +function getEnvFile(suffix) { + if (suffix && suffix[0] !== '.') { + suffix = '.' + suffix; + } + + if (suffix === null) { + return; + } + + let fileName = helpers.root(`src/environments/environment${suffix}.ts`); + if (fs.existsSync(fileName)) { + return fileName; + } else if (fs.existsSync(fileName = helpers.root('src/environments/environment.ts'))) { + console.warn(`Could not find environment file with suffix ${suffix}, loading default environment file`); + return fileName; + } else { + throw new Error('Environment file not found.') + } +} + +/** + * Read the tsconfig to determine if we should prefer ES2015 modules. + * Load rxjs path aliases. + * https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking + * @param supportES2015 Set to true when the output of typescript is >= ES6 + */ +function rxjsAlias(supportES2015) { + try { + const rxjsPathMappingImport = supportES2015 ? 'rxjs/_esm2015/path-mapping' : 'rxjs/_esm5/path-mapping'; + const rxPaths = require(rxjsPathMappingImport); + return rxPaths(helpers.root('node_modules')); + } catch (e) { + return {}; + } +} + +function ngcWebpackSetup(prod, metadata) { + if (!metadata) { + metadata = DEFAULT_METADATA; + } + + const buildOptimizer = prod && metadata.AOT; + const sourceMap = true; // TODO: apply based on tsconfig value? + const ngcWebpackPluginOptions = { + skipCodeGeneration: !metadata.AOT, + sourceMap + }; + + const environment = getEnvFile(metadata.envFileSuffix); + if (environment) { + ngcWebpackPluginOptions.hostReplacementPaths = { + [helpers.root('src/environments/environment.ts')]: environment + } + } + + if (!prod && metadata.WATCH) { + // Force commonjs module format for TS on dev watch builds. + ngcWebpackPluginOptions.compilerOptions = { + module: 'commonjs' + }; + } + + const buildOptimizerLoader = { + loader: '@angular-devkit/build-optimizer/webpack-loader', + options: { + sourceMap + } + }; + + const loaders = [ + { + test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, + use: buildOptimizer ? [ buildOptimizerLoader, '@ngtools/webpack' ] : [ '@ngtools/webpack' ] + }, + ...buildOptimizer + ? [ { test: /\.js$/, use: [ buildOptimizerLoader ] } ] + : [] + ]; + + return { + loaders, + plugin: ngcWebpackPluginOptions + }; +} + + +exports.DEFAULT_METADATA = DEFAULT_METADATA; +exports.supportES2015 = supportES2015; +exports.readTsConfig = readTsConfig; +exports.getEnvFile = getEnvFile; +exports.rxjsAlias = rxjsAlias; +exports.ngcWebpackSetup = ngcWebpackSetup; diff --git a/config/config.common.json b/config/config.common.json new file mode 100644 index 0000000000..5ab0d19606 --- /dev/null +++ b/config/config.common.json @@ -0,0 +1,4 @@ +{ + "title": "Angular Starter by @gdi2290 from @TipeIO", + "description": "An Angular starter kit featuring Angular 5, Ahead of Time Compile, Router, Forms, Http, Services, Tests, E2E), Karma, Protractor, Jasmine, Istanbul, TypeScript, @types, TsLint, Codelyzer, Hot Module Replacement, and Webpack by Tipe.io" +} diff --git a/config/config.dev.json b/config/config.dev.json new file mode 100644 index 0000000000..2f8218edd0 --- /dev/null +++ b/config/config.dev.json @@ -0,0 +1,10 @@ +{ + "firebase": { + "apiKey": "", + "authDomain": "XXXXXX.firebaseapp.com", + "databaseURL": "https://XXXXXX.firebaseio.com", + "projectId": "XXXXXX", + "storageBucket": "XXXXXX.appspot.com", + "messagingSenderId": "000000000000" + } +} diff --git a/config/config.prod.json b/config/config.prod.json new file mode 100644 index 0000000000..1ac6fc2768 --- /dev/null +++ b/config/config.prod.json @@ -0,0 +1,11 @@ +{ + "firebase": { + "apiKey": "", + "authDomain": "XXXXXX.firebaseapp.com", + "databaseURL": "https://XXXXXX.firebaseio.com", + "projectId": "XXXXXX", + "storageBucket": "XXXXXX.appspot.com", + "messagingSenderId": "000000000000" + }, + "gtmKey" : "GTM-XXXXXXX" +} diff --git a/config/empty.js b/config/empty.js index 7578e4f565..6bc5423e97 100644 --- a/config/empty.js +++ b/config/empty.js @@ -1,4 +1,7 @@ module.exports = { + hmrModule: function(ngmodule) { + return ngmodule; + }, NgProbeToken: {}, HmrState: function() {}, _createConditionalRootRenderer: function(rootRenderer, extraTokens, coreTokens) { diff --git a/config/helpers.js b/config/helpers.js index 6d73632dca..aae072eafc 100644 --- a/config/helpers.js +++ b/config/helpers.js @@ -1,7 +1,7 @@ /** - * @author: @AngularClass + * @author: tipe.io */ -var path = require('path'); +const path = require('path'); const EVENT = process.env.npm_lifecycle_event || ''; @@ -22,6 +22,7 @@ function isWebpackDevServer() { return process.argv[1] && !! (/webpack-dev-server/.exec(process.argv[1])); } + var root = path.join.bind(path, ROOT); exports.hasProcessFlag = hasProcessFlag; diff --git a/config/html-elements-plugin/index.js b/config/html-elements-plugin/index.js index 78d010d4a2..c4be4909eb 100644 --- a/config/html-elements-plugin/index.js +++ b/config/html-elements-plugin/index.js @@ -1,30 +1,3 @@ -function HtmlElementsPlugin(locations) { - this.locations = locations; -} - -HtmlElementsPlugin.prototype.apply = function(compiler) { - var self = this; - compiler.plugin('compilation', function(compilation) { - compilation.options.htmlElements = compilation.options.htmlElements || {}; - - compilation.plugin('html-webpack-plugin-before-html-generation', function(htmlPluginData, callback) { - const locations = self.locations; - - if (locations) { - const publicPath = htmlPluginData.assets.publicPath; - - Object.getOwnPropertyNames(locations).forEach(function(loc) { - compilation.options.htmlElements[loc] = getHtmlElementString(locations[loc], publicPath); - }); - } - - - callback(null, htmlPluginData); - }); - }); - -}; - const RE_ENDS_WITH_BS = /\/$/; /** @@ -49,8 +22,10 @@ function createTag(tagName, attrMap, publicPath) { } const attributes = Object.getOwnPropertyNames(attrMap) - .filter(function(name) { return name[0] !== '='; } ) - .map(function(name) { + .filter(function (name) { + return name[0] !== '='; + }) + .map(function (name) { var value = attrMap[name]; if (publicPath) { @@ -98,16 +73,50 @@ function createTag(tagName, attrMap, publicPath) { */ function getHtmlElementString(dataSource, publicPath) { return Object.getOwnPropertyNames(dataSource) - .map(function(name) { + .map(function (name) { if (Array.isArray(dataSource[name])) { - return dataSource[name].map(function(attrs) { return createTag(name, attrs, publicPath); } ); + return dataSource[name].map(function (attrs) { + return createTag(name, attrs, publicPath); + }); } else { - return [ createTag(name, dataSource[name], publicPath) ]; + return [createTag(name, dataSource[name], publicPath)]; } }) - .reduce(function(arr, curr) { + .reduce(function (arr, curr) { return arr.concat(curr); }, []) .join('\n\t'); } + +class HtmlElementsPlugin { + constructor(locations) { + this.locations = locations; + } + + /* istanbul ignore next: this would be integration tests */ + apply(compiler) { + compiler.hooks.compilation.tap('HtmlElementsPlugin', compilation => { + compilation.options.htmlElements = compilation.options.htmlElements || {}; + compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync('HtmlElementsPlugin', + (htmlPluginData, callback) => { + + const locations = this.locations; + if (locations) { + const publicPath = htmlPluginData.assets.publicPath; + + Object.getOwnPropertyNames(locations).forEach(function (loc) { + + compilation.options.htmlElements[loc] = getHtmlElementString(locations[loc], publicPath); + }); + } + + // return htmlPluginData; + callback(null, htmlPluginData); + } + ); + }); + } +} + module.exports = HtmlElementsPlugin; + diff --git a/config/karma.conf.js b/config/karma.conf.js index 46799a5c97..f83c39b604 100644 --- a/config/karma.conf.js +++ b/config/karma.conf.js @@ -1,11 +1,11 @@ /** - * @author: @AngularClass + * @author: tipe.io */ module.exports = function (config) { - var testWebpackConfig = require('./webpack.test.js')({ env: 'test' }); + const testWebpackConfig = require('./webpack.test.js')({ env: 'test' }); - var configuration = { + const configuration = { /** * Base path that will be used to resolve all patterns (e.g. files, exclude). @@ -74,7 +74,7 @@ module.exports = function (config) { * webpack-dev-middleware configuration * i.e. */ - noInfo: true, + logLevel: 'warn', /** * and use stats to turn off verbose output */ @@ -113,20 +113,20 @@ module.exports = function (config) { /** * enable / disable watching file and executing tests whenever any file changes */ - autoWatch: false, + autoWatch: true, /** * start these browsers * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher */ browsers: [ - 'Chrome' + 'ChromeTravisCi' ], customLaunchers: { ChromeTravisCi: { - base: 'Chrome', - flags: ['--no-sandbox'] + base: 'ChromeHeadless', + flags: ['--no-sandbox', '--disable-gpu'] } }, @@ -134,9 +134,40 @@ module.exports = function (config) { * Continuous Integration mode * if true, Karma captures browsers, runs the tests and exits */ - singleRun: true + singleRun: false, + + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + + /** + * For slower machines you may need to have a longer browser + * wait time . Uncomment the line below if required. + */ + // browserNoActivityTimeout: 30000 + }; + // Optional Sonar Qube Reporter + if (process.env.SONAR_QUBE) { + + // SonarQube reporter plugin configuration + configuration.sonarQubeUnitReporter = { + sonarQubeVersion: '5.x', + outputFile: 'reports/ut_report.xml', + overrideTestDescription: true, + testPath: 'src/app', + testFilePattern: '.spec.ts', + useBrowserName: false + }; + + // Additional lcov format required for + // sonarqube + configuration.remapCoverageReporter.lcovonly = './coverage/coverage.lcov'; + + configuration.reporters.push('sonarqubeUnit'); + } + if (process.env.TRAVIS) { configuration.browsers = [ 'ChromeTravisCi' diff --git a/config/nginx-custom.conf b/config/nginx-custom.conf new file mode 100644 index 0000000000..8e1dbe59d9 --- /dev/null +++ b/config/nginx-custom.conf @@ -0,0 +1,19 @@ +server { + listen 80; + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 1100; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 5; + + root /usr/share/nginx/html; + + location / { + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} diff --git a/config/protractor.conf.js b/config/protractor.conf.js index acaa95cb4d..e3cf3f9d03 100644 --- a/config/protractor.conf.js +++ b/config/protractor.conf.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ require('ts-node/register'); @@ -17,24 +17,23 @@ exports.config = { ], exclude: [], - framework: 'jasmine2', + framework: 'jasmine', - allScriptsTimeout: 110000, + allScriptsTimeout: 11000, jasmineNodeOpts: { showTiming: true, showColors: true, isVerbose: false, includeStackTrace: false, - defaultTimeoutInterval: 400000 + defaultTimeoutInterval: 40000 }, - directConnect: true, + directConnect: true, capabilities: { - 'browserName': 'chrome', - 'chromeOptions': { - //'args': ["--headless", "--disable-gpu", "--window-size=1280x800", "--no-sandbox"] - 'args': ['show-fps-counter=true'] + browserName: 'chrome', + chromeOptions: { + args: [ "--headless", "--disable-gpu", "--window-size=800x600", "--no-sandbox" ] } }, diff --git a/config/spec-bundle.js b/config/spec-bundle.js index d4ca968ddb..9906c2179e 100644 --- a/config/spec-bundle.js +++ b/config/spec-bundle.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ /** @@ -24,11 +24,6 @@ require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14 require('zone.js/dist/async-test'); require('zone.js/dist/fake-async-test'); -/** - * RxJS - */ -require('rxjs/Rx'); - var testing = require('@angular/core/testing'); var browser = require('@angular/platform-browser-dynamic/testing'); diff --git a/config/webpack.common.js b/config/webpack.common.js index 4c99f34942..23d6648a0a 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -1,8 +1,7 @@ /** - * @author: @AngularClass + * @author: tipe.io */ -const webpack = require('webpack'); const helpers = require('./helpers'); /** @@ -10,75 +9,62 @@ const helpers = require('./helpers'); * * problem with copy-webpack-plugin */ -const AssetsPlugin = require('assets-webpack-plugin'); -const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin'); -const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin'); -const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); +const DefinePlugin = require('webpack/lib/DefinePlugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; const HtmlElementsPlugin = require('./html-elements-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const InlineManifestWebpackPlugin = require('inline-manifest-webpack-plugin'); -const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); +const WebpackInlineManifestPlugin = require('webpack-inline-manifest-plugin'); const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin'); -const ngcWebpack = require('ngc-webpack'); -//const PreloadWebpackPlugin = require('preload-webpack-plugin'); +const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin; -/** - * Webpack Constants - */ -const HMR = helpers.hasProcessFlag('hot'); -const AOT = process.env.BUILD_AOT || helpers.hasNpmFlag('aot'); -const METADATA = { - title: 'Angular2 Webpack Starter by @gdi2290 from @AngularClass', - baseUrl: '/', - isDevServer: helpers.isWebpackDevServer(), - HMR: HMR -}; +const buildUtils = require('./build-utils'); /** * Webpack configuration * - * See: http://webpack.github.io/docs/configuration.html#cli + * See: https://webpack.js.org/configuration/ */ -module.exports = function (options) { - isProd = options.env === 'production'; - return { +module.exports = function(options) { + const isProd = options.env === 'production'; + const APP_CONFIG = require(process.env.ANGULAR_CONF_FILE || (isProd ? './config.prod.json' : './config.dev.json')); - /** - * Cache generated modules and chunks to improve performance for multiple incremental builds. - * This is enabled by default in watch mode. - * You can pass false to disable it. - * - * See: http://webpack.github.io/docs/configuration.html#cache - */ - //cache: false, + const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, options.metadata || {}); + const GTM_API_KEY = process.env.GTM_API_KEY || APP_CONFIG.gtmKey; + const ngcWebpackConfig = buildUtils.ngcWebpackSetup(isProd, METADATA); + const supportES2015 = buildUtils.supportES2015(METADATA.tsConfigPath); + + const entry = { + polyfills: './src/polyfills.browser.ts', + main: './src/main.browser.ts' + }; + + Object.assign(ngcWebpackConfig.plugin, { + tsConfigPath: METADATA.tsConfigPath, + mainPath: entry.main + }); + + return { /** * The entry point for the bundle * Our Angular.js app * - * See: http://webpack.github.io/docs/configuration.html#entry + * See: https://webpack.js.org/configuration/entry-context/#entry */ - entry: { - - 'polyfills': './src/polyfills.browser.ts', - 'main': AOT ? './src/main.browser.aot.ts' : - './src/main.browser.ts' - - }, + entry: entry, /** * Options affecting the resolving of modules. * - * See: http://webpack.github.io/docs/configuration.html#resolve + * See: https://webpack.js.org/configuration/resolve/ */ resolve: { + mainFields: [...(supportES2015 ? ['es2015'] : []), 'browser', 'module', 'main'], /** * An array of extensions that should be used to resolve modules. * - * See: http://webpack.github.io/docs/configuration.html#resolve-extensions + * See: https://webpack.js.org/configuration/resolve/#resolve-extensions */ extensions: ['.ts', '.js', '.json'], @@ -87,70 +73,36 @@ module.exports = function (options) { */ modules: [helpers.root('src'), helpers.root('node_modules')], + /** + * Add support for lettable operators. + * + * For existing codebase a refactor is required. + * All rxjs operator imports (e.g. `import 'rxjs/add/operator/map'` or `import { map } from `rxjs/operator/map'` + * must change to `import { map } from 'rxjs/operators'` (note that all operators are now under that import. + * Additionally some operators have changed to to JS keyword constraints (do => tap, catch => catchError) + * + * Remember to use the `pipe()` method to chain operators, this functinoally makes lettable operators similar to + * the old operators usage paradigm. + * + * For more details see: + * https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#build-and-treeshaking + * + * If you are not planning on refactoring your codebase (or not planning on using imports from `rxjs/operators` + * comment out this line. + * + * BE AWARE that not using lettable operators will probably result in significant payload added to your bundle. + */ + alias: buildUtils.rxjsAlias(supportES2015) }, /** * Options affecting the normal modules. * - * See: http://webpack.github.io/docs/configuration.html#module + * See: https://webpack.js.org/configuration/module/ */ module: { - rules: [ - - /** - * Typescript loader support for .ts - * - * Component Template/Style integration using `angular2-template-loader` - * Angular 2 lazy loading (async routes) via `ng-router-loader` - * - * `ng-router-loader` expects vanilla JavaScript code, not TypeScript code. This is why the - * order of the loader matter. - * - * See: https://github.com/s-panferov/awesome-typescript-loader - * See: https://github.com/TheLarkInn/angular2-template-loader - * See: https://github.com/shlomiassaf/ng-router-loader - */ - { - test: /\.ts$/, - use: [ - { - loader: '@angularclass/hmr-loader', - options: { - pretty: !isProd, - prod: isProd - } - }, - { - /** - * MAKE SURE TO CHAIN VANILLA JS CODE, I.E. TS COMPILATION OUTPUT. - */ - loader: 'ng-router-loader', - options: { - loader: 'async-import', - genDir: 'compiled', - aot: AOT - } - }, - { - loader: 'awesome-typescript-loader', - options: { - configFileName: 'tsconfig.webpack.json', - useCache: !isProd - } - }, - { - loader: 'ngc-webpack', - options: { - disable: !AOT, - } - }, - { - loader: 'angular2-template-loader' - } - ], - exclude: [/\.(spec|e2e)\.ts$/] - }, + ...ngcWebpackConfig.loaders, /** * To string and css loader support for *.css files (from Angular components) @@ -200,81 +152,34 @@ module.exports = function (options) { test: /\.(eot|woff2?|svg|ttf)([\?]?.*)$/, use: 'file-loader' } - - ], - + ] }, /** * Add additional plugins to the compiler. * - * See: http://webpack.github.io/docs/configuration.html#plugins + * See: https://webpack.js.org/configuration/plugins/ */ plugins: [ - // Use for DLLs - // new AssetsPlugin({ - // path: helpers.root('dist'), - // filename: 'webpack-assets.json', - // prettyPrint: true - // }), - /** - * Plugin: ForkCheckerPlugin - * Description: Do type checking in a separate process, so webpack doesn't need to wait. + * Plugin: DefinePlugin + * Description: Define free variables. + * Useful for having development builds with debug logging or adding global constants. * - * See: https://github.com/s-panferov/awesome-typescript-loader#forkchecker-boolean-defaultfalse - */ - new CheckerPlugin(), - /** - * Plugin: CommonsChunkPlugin - * Description: Shares common code between the pages. - * It identifies common modules and put them into a commons chunk. + * Environment helpers * - * See: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin - * See: https://github.com/webpack/docs/wiki/optimization#multi-page-app + * See: https://webpack.js.org/plugins/define-plugin/ */ - new CommonsChunkPlugin({ - name: 'polyfills', - chunks: ['polyfills'] + // NOTE: when adding more properties make sure you include them in custom-typings.d.ts + new DefinePlugin({ + ENV: JSON.stringify(METADATA.ENV), + HMR: METADATA.HMR, + AOT: METADATA.AOT, + 'process.env.ENV': JSON.stringify(METADATA.ENV), + 'process.env.NODE_ENV': JSON.stringify(METADATA.ENV), + 'process.env.HMR': METADATA.HMR + // 'FIREBASE_CONFIG': JSON.stringify(APP_CONFIG.firebase), }), - /** - * This enables tree shaking of the vendor modules - */ - // new CommonsChunkPlugin({ - // name: 'vendor', - // chunks: ['main'], - // minChunks: module => /node_modules/.test(module.resource) - // }), - /** - * Specify the correct order the scripts will be injected in - */ - // new CommonsChunkPlugin({ - // name: ['polyfills', 'vendor'].reverse() - // }), - // new CommonsChunkPlugin({ - // name: ['manifest'], - // minChunks: Infinity, - // }), - - /** - * Plugin: ContextReplacementPlugin - * Description: Provides context to Angular's use of System.import - * - * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin - * See: https://github.com/angular/angular/issues/11580 - */ - new ContextReplacementPlugin( - /** - * The (\\|\/) piece accounts for path separators in *nix and Windows - */ - /angular(\\|\/)core(\\|\/)@angular/, - helpers.root('src'), // location of your src - { - /** - * Your Angular Async Route paths relative to this root directory - */ - } - ), /** * Plugin: CopyWebpackPlugin @@ -284,32 +189,11 @@ module.exports = function (options) { * * See: https://www.npmjs.com/package/copy-webpack-plugin */ - new CopyWebpackPlugin([ - { from: 'src/assets', to: 'assets' }, - { from: 'src/meta'} - ], - isProd ? { ignore: [ 'mock-data/**/*' ] } : undefined + new CopyWebpackPlugin( + [{ from: 'src/assets', to: 'assets' }, { from: 'src/meta' }], + isProd ? { ignore: ['mock-data/**/*'] } : undefined ), - /* - * Plugin: PreloadWebpackPlugin - * Description: Preload is a web standard aimed at improving - * performance and granular loading of resources. - * - * See: https://github.com/GoogleChrome/preload-webpack-plugin - */ - //new PreloadWebpackPlugin({ - // rel: 'preload', - // as: 'script', - // include: ['polyfills', 'vendor', 'main'].reverse(), - // fileBlacklist: ['.css', '.map'] - //}), - //new PreloadWebpackPlugin({ - // rel: 'prefetch', - // as: 'script', - // include: 'asyncChunks' - //}), - /* * Plugin: HtmlWebpackPlugin * Description: Simplifies creation of HTML files to serve your webpack bundles. @@ -321,12 +205,24 @@ module.exports = function (options) { new HtmlWebpackPlugin({ template: 'src/index.html', title: METADATA.title, - chunksSortMode: 'dependency', + chunksSortMode: function(a, b) { + const entryPoints = ['inline', 'polyfills', 'sw-register', 'styles', 'vendor', 'main']; + return entryPoints.indexOf(a.names[0]) - entryPoints.indexOf(b.names[0]); + }, metadata: METADATA, - inject: 'body' + gtmKey: GTM_API_KEY, + inject: 'body', + xhtml: true, + minify: isProd + ? { + caseSensitive: true, + collapseWhitespace: true, + keepClosingSlash: true + } + : false }), - - /** + + /** * Plugin: ScriptExtHtmlWebpackPlugin * Description: Enhances html-webpack-plugin functionality * with different deployment options for your scripts including: @@ -334,7 +230,7 @@ module.exports = function (options) { * See: https://github.com/numical/script-ext-html-webpack-plugin */ new ScriptExtHtmlWebpackPlugin({ - sync: /polyfills|vendor/, + sync: /inline|polyfills|vendor/, defaultAttribute: 'async', preload: [/polyfills|vendor|main/], prefetch: [/chunk/] @@ -366,39 +262,22 @@ module.exports = function (options) { headTags: require('./head-config.common') }), - /** - * Plugin LoaderOptionsPlugin (experimental) - * - * See: https://gist.github.com/sokra/27b24881210b56bbaff7 - */ - new LoaderOptionsPlugin({}), - - new ngcWebpack.NgcWebpackPlugin({ - /** - * If false the plugin is a ghost, it will not perform any action. - * This property can be used to trigger AOT on/off depending on your build target (prod, staging etc...) - * - * The state can not change after initializing the plugin. - * @default true - */ - disabled: !AOT, - tsConfig: helpers.root('tsconfig.webpack.json'), - }), + new AngularCompilerPlugin(ngcWebpackConfig.plugin), /** - * Plugin: InlineManifestWebpackPlugin + * Plugin: WebpackInlineManifestPlugin * Inline Webpack's manifest.js in index.html * - * https://github.com/szrenwei/inline-manifest-webpack-plugin + * https://github.com/almothafar/webpack-inline-manifest-plugin */ - new InlineManifestWebpackPlugin(), + new WebpackInlineManifestPlugin() ], /** * Include polyfills or mocks for various node stuff * Description: Node configuration * - * See: https://webpack.github.io/docs/configuration.html#node + * See: https://webpack.js.org/configuration/node/ */ node: { global: true, @@ -408,6 +287,5 @@ module.exports = function (options) { clearImmediate: false, setImmediate: false } - }; -} +}; diff --git a/config/webpack.dev.js b/config/webpack.dev.js index 6490cdd4c5..ecabb3a16f 100755 --- a/config/webpack.dev.js +++ b/config/webpack.dev.js @@ -1,66 +1,49 @@ /** - * @author: @AngularClass + * @author: tipe.io */ const helpers = require('./helpers'); +const buildUtils = require('./build-utils'); const webpackMerge = require('webpack-merge'); // used to merge webpack configs -// const webpackMergeDll = webpackMerge.strategy({plugins: 'replace'}); const commonConfig = require('./webpack.common.js'); // the settings that are common to prod and dev /** * Webpack Plugins */ -const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); -const DefinePlugin = require('webpack/lib/DefinePlugin'); -const NamedModulesPlugin = require('webpack/lib/NamedModulesPlugin'); const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); -/** - * Webpack Constants - */ -const ENV = process.env.ENV = process.env.NODE_ENV = 'development'; -const HOST = process.env.HOST || 'localhost'; -const PORT = process.env.PORT || 3000; -const PUBLIC = process.env.PUBLIC_DEV || undefined; -const HMR = helpers.hasProcessFlag('hot'); -const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, { - host: HOST, - port: PORT, - public: PUBLIC, - ENV: ENV, - HMR: HMR -}); - - -// const DllBundlesPlugin = require('webpack-dll-bundles-plugin').DllBundlesPlugin; - /** * Webpack configuration * - * See: http://webpack.github.io/docs/configuration.html#cli + * See: https://webpack.js.org/configuration/ */ -module.exports = function (options) { - return webpackMerge(commonConfig({env: ENV}), { +module.exports = function(options) { + const ENV = (process.env.ENV = process.env.NODE_ENV = 'development'); + const HOST = process.env.HOST || 'localhost'; + const PORT = process.env.PORT || 3000; + + const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, { + host: HOST, + port: PORT, + ENV: ENV, + HMR: helpers.hasProcessFlag('hot'), + PUBLIC: process.env.PUBLIC_DEV || HOST + ':' + PORT + }); - /** - * Developer tool to enhance debugging - * - * See: http://webpack.github.io/docs/configuration.html#devtool - * See: https://github.com/webpack/docs/wiki/build-performance#sourcemaps - */ - devtool: 'cheap-module-source-map', + return webpackMerge(commonConfig({ env: ENV, metadata: METADATA }), { + mode: 'development', + devtool: 'inline-source-map', /** * Options affecting the output of the compilation. * - * See: http://webpack.github.io/docs/configuration.html#output + * See: https://webpack.js.org/configuration/output/ */ output: { - /** * The output directory as absolute path (required). * - * See: http://webpack.github.io/docs/configuration.html#output-path + * See: https://webpack.js.org/configuration/output/#output-path */ path: helpers.root('dist'), @@ -68,7 +51,7 @@ module.exports = function (options) { * Specifies the name of each output file on disk. * IMPORTANT: You must not specify an absolute path here! * - * See: http://webpack.github.io/docs/configuration.html#output-filename + * See: https://webpack.js.org/configuration/output/#output-filename */ filename: '[name].bundle.js', @@ -76,25 +59,23 @@ module.exports = function (options) { * The filename of the SourceMaps for the JavaScript files. * They are inside the output.path directory. * - * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename + * See: https://webpack.js.org/configuration/output/#output-sourcemapfilename */ sourceMapFilename: '[file].map', /** The filename of non-entry chunks as relative path * inside the output.path directory. * - * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename + * See: https://webpack.js.org/configuration/output/#output-chunkfilename */ chunkFilename: '[id].chunk.js', library: 'ac_[name]', - libraryTarget: 'var', + libraryTarget: 'var' }, module: { - rules: [ - /** * Css loader support for *.css files (styles directory only) * Loads external css styles into the DOM, supports HMR @@ -115,88 +96,11 @@ module.exports = function (options) { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'], include: [helpers.root('src', 'styles')] - }, - + } ] - }, plugins: [ - - /** - * Plugin: DefinePlugin - * Description: Define free variables. - * Useful for having development builds with debug logging or adding global constants. - * - * Environment helpers - * - * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin - * - * NOTE: when adding more properties, make sure you include them in custom-typings.d.ts - */ - new DefinePlugin({ - 'ENV': JSON.stringify(METADATA.ENV), - 'HMR': METADATA.HMR, - 'process.env': { - 'ENV': JSON.stringify(METADATA.ENV), - 'NODE_ENV': JSON.stringify(METADATA.ENV), - 'HMR': METADATA.HMR, - } - }), - - // new DllBundlesPlugin({ - // bundles: { - // polyfills: [ - // 'core-js', - // { - // name: 'zone.js', - // path: 'zone.js/dist/zone.js' - // }, - // { - // name: 'zone.js', - // path: 'zone.js/dist/long-stack-trace-zone.js' - // }, - // ], - // vendor: [ - // '@angular/platform-browser', - // '@angular/platform-browser-dynamic', - // '@angular/core', - // '@angular/common', - // '@angular/forms', - // '@angular/http', - // '@angular/router', - // '@angularclass/hmr', - // 'rxjs', - // ] - // }, - // dllDir: helpers.root('dll'), - // webpackConfig: webpackMergeDll(commonConfig({env: ENV}), { - // devtool: 'cheap-module-source-map', - // plugins: [] - // }) - // }), - - /** - * Plugin: AddAssetHtmlPlugin - * Description: Adds the given JS or CSS file to the files - * Webpack knows about, and put it into the list of assets - * html-webpack-plugin injects into the generated html. - * - * See: https://github.com/SimenB/add-asset-html-webpack-plugin - */ - // new AddAssetHtmlPlugin([ - // { filepath: helpers.root(`dll/${DllBundlesPlugin.resolveFile('polyfills')}`) }, - // { filepath: helpers.root(`dll/${DllBundlesPlugin.resolveFile('vendor')}`) } - // ]), - - /** - * Plugin: NamedModulesPlugin (experimental) - * Description: Uses file names as module name. - * - * See: https://github.com/webpack/webpack/commit/a04ffb928365b19feb75087c63f13cadfc08e1eb - */ - // new NamedModulesPlugin(), - /** * Plugin LoaderOptionsPlugin (experimental) * @@ -204,11 +108,8 @@ module.exports = function (options) { */ new LoaderOptionsPlugin({ debug: true, - options: { - - } - }), - + options: {} + }) ], /** @@ -217,12 +118,13 @@ module.exports = function (options) { * The server emits information about the compilation state to the client, * which reacts to those events. * - * See: https://webpack.github.io/docs/webpack-dev-server.html + * See: https://webpack.js.org/configuration/dev-server/ */ devServer: { port: METADATA.port, host: METADATA.host, - public: METADATA.public, + hot: METADATA.HMR, + public: METADATA.PUBLIC, historyApiFallback: true, watchOptions: { // if you're using Docker you may need this @@ -231,10 +133,10 @@ module.exports = function (options) { ignored: /node_modules/ }, /** - * Here you can access the Express app object and add your own custom middleware to it. - * - * See: https://webpack.github.io/docs/webpack-dev-server.html - */ + * Here you can access the Express app object and add your own custom middleware to it. + * + * See: https://webpack.js.org/configuration/dev-server/ + */ setup: function(app) { // For example, to define custom handlers for some paths: // app.get('/some/path', function(req, res) { @@ -247,7 +149,7 @@ module.exports = function (options) { * Include polyfills or mocks for various node stuff * Description: Node configuration * - * See: https://webpack.github.io/docs/configuration.html#node + * See: https://webpack.js.org/configuration/node/ */ node: { global: true, @@ -255,8 +157,8 @@ module.exports = function (options) { process: true, module: false, clearImmediate: false, - setImmediate: false + setImmediate: false, + fs: 'empty' } - }); -} +}; diff --git a/config/webpack.github-deploy.js b/config/webpack.github-deploy.js index 0c5218cc52..7211e2b866 100644 --- a/config/webpack.github-deploy.js +++ b/config/webpack.github-deploy.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ const fs = require('fs'); const path = require('path'); @@ -14,7 +14,7 @@ const GIT_REMOTE_NAME = 'origin'; const COMMIT_MESSAGE = 'Updates'; const GH_REPO_NAME = ghDeploy.getRepoName(GIT_REMOTE_NAME); -module.exports = function (options) { +module.exports = function(options) { const webpackConfigFactory = ghDeploy.getWebpackConfigModule(options); // the settings that are common to prod and dev const webpackConfig = webpackConfigFactory(options); @@ -24,57 +24,58 @@ module.exports = function (options) { ghDeploy.replaceHtmlWebpackPlugin(webpackConfig.plugins, GH_REPO_NAME); return webpackMerge(webpackConfig, { - output: { - /** - * The public path is set to the REPO name. - * - * `HtmlElementsPlugin` will add it to all resources url's created by it. - * `HtmlWebpackPlugin` will add it to all webpack bundels/chunks. - * - * In theory publicPath shouldn't be used since the browser should automatically prefix the - * `baseUrl` into all URLs, however this is not the case when the URL is absolute (start with /) - * - * It's important to prefix & suffix the repo name with a slash (/). - * Prefixing so every resource will be absolute (otherwise it will be url.com/repoName/repoName... - * Suffixing since chunks will not do it automatically (testes against about page) - */ - publicPath: '/' + GH_REPO_NAME + '/' + ghDeploy.safeUrl(webpackConfig.output.publicPath) - }, - plugins: [ - function() { - this.plugin('done', function(stats) { - console.log('Starting deployment to GitHub.'); + output: { + /** + * The public path is set to the REPO name. + * + * `HtmlElementsPlugin` will add it to all resources url's created by it. + * `HtmlWebpackPlugin` will add it to all webpack bundels/chunks. + * + * In theory publicPath shouldn't be used since the browser should automatically prefix the + * `baseUrl` into all URLs, however this is not the case when the URL is absolute (start with /) + * + * It's important to prefix & suffix the repo name with a slash (/). + * Prefixing so every resource will be absolute (otherwise it will be url.com/repoName/repoName... + * Suffixing since chunks will not do it automatically (testes against about page) + */ + publicPath: '/' + GH_REPO_NAME + '/' + ghDeploy.safeUrl(webpackConfig.output.publicPath) + }, - const logger = function (msg) { - console.log(msg); - }; + plugins: [ + function() { + this.plugin('done', function(stats) { + console.log('Starting deployment to GitHub.'); - const options = { - logger: logger, - remote: GIT_REMOTE_NAME, - message: COMMIT_MESSAGE, - dotfiles: true // for .nojekyll - }; - /** - * Since GitHub moved to Jekyll 3.3, their server ignores the "node_modules" and "vendors" folder by default. - * but, as of now, it also ignores "vendors*" files. - * This means vendor.bundle.js or vendor.[chunk].bundle.js will return 404. - * this is the fix for now. - */ - fs.writeFileSync(path.join(webpackConfig.output.path, '.nojekyll'), ''); + const logger = function(msg) { + console.log(msg); + }; - const ghpages = require('gh-pages'); - ghpages.publish(webpackConfig.output.path, options, function(err) { - if (err) { - console.log('GitHub deployment done. STATUS: ERROR.'); - throw err; - } else { - console.log('GitHub deployment done. STATUS: SUCCESS.'); - } - }); - }); - } - ] - }); + const options = { + logger: logger, + remote: GIT_REMOTE_NAME, + message: COMMIT_MESSAGE, + dotfiles: true // for .nojekyll + }; + /** + * Since GitHub moved to Jekyll 3.3, their server ignores the "node_modules" and "vendors" folder by default. + * but, as of now, it also ignores "vendors*" files. + * This means vendor.bundle.js or vendor.[chunk].bundle.js will return 404. + * this is the fix for now. + */ + fs.writeFileSync(path.join(webpackConfig.output.path, '.nojekyll'), ''); + + const ghpages = require('gh-pages'); + ghpages.publish(webpackConfig.output.path, options, function(err) { + if (err) { + console.log('GitHub deployment done. STATUS: ERROR.'); + throw err; + } else { + console.log('GitHub deployment done. STATUS: SUCCESS.'); + } + }); + }); + } + ] + }); }; diff --git a/config/webpack.prod.js b/config/webpack.prod.js index 7ae0ce9bca..99c4cc947b 100644 --- a/config/webpack.prod.js +++ b/config/webpack.prod.js @@ -1,69 +1,83 @@ /** - * @author: @AngularClass + * @author: tipe.io */ - const helpers = require('./helpers'); +const buildUtils = require('./build-utils'); + /** * Used to merge webpack configs -*/ + */ const webpackMerge = require('webpack-merge'); + /** * The settings that are common to prod and dev -*/ + */ const commonConfig = require('./webpack.common.js'); /** * Webpack Plugins */ -const DefinePlugin = require('webpack/lib/DefinePlugin'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const HashedModuleIdsPlugin = require('webpack/lib/HashedModuleIdsPlugin') -const IgnorePlugin = require('webpack/lib/IgnorePlugin'); -const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); -const NormalModuleReplacementPlugin = require('webpack/lib/NormalModuleReplacementPlugin'); -const ProvidePlugin = require('webpack/lib/ProvidePlugin'); -const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); -const OptimizeJsPlugin = require('optimize-js-plugin'); -/** - * Webpack Constants +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const HashedModuleIdsPlugin = require('webpack/lib/HashedModuleIdsPlugin'); +const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); + +/*** + * Ref: https://github.com/mishoo/UglifyJS2/tree/harmony#minify-options + * @param supportES2015 + * @param enableCompress disabling compress could improve the performance, see https://github.com/webpack/webpack/issues/4558#issuecomment-352255789 + * @returns {{ecma: number, warnings: boolean, ie8: boolean, mangle: boolean, compress: {pure_getters: boolean, passes: number}, output: {ascii_only: boolean, comments: boolean}}} */ -const ENV = process.env.NODE_ENV = process.env.ENV = 'production'; -const HOST = process.env.HOST || 'localhost'; -const PORT = process.env.PORT || 8080; -const METADATA = webpackMerge(commonConfig({ - env: ENV -}).metadata, { - host: HOST, - port: PORT, - ENV: ENV, - HMR: false -}); +function getUglifyOptions(supportES2015, enableCompress) { + const uglifyCompressOptions = { + pure_getters: true /* buildOptimizer */, + // PURE comments work best with 3 passes. + // See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926. + passes: 2 /* buildOptimizer */ + }; + + return { + ecma: supportES2015 ? 6 : 5, + warnings: false, // TODO verbose based on option? + ie8: false, + mangle: true, + compress: enableCompress ? uglifyCompressOptions : false, + output: { + ascii_only: true, + comments: false + } + }; +} -module.exports = function (env) { - return webpackMerge(commonConfig({ - env: ENV - }), { +module.exports = function(env) { + const ENV = (process.env.NODE_ENV = process.env.ENV = 'production'); + const supportES2015 = buildUtils.supportES2015(buildUtils.DEFAULT_METADATA.tsConfigPath); + const sourceMapEnabled = process.env.SOURCE_MAP === '1'; + const METADATA = Object.assign({}, buildUtils.DEFAULT_METADATA, { + host: process.env.HOST || 'localhost', + port: process.env.PORT || 8080, + ENV: ENV, + HMR: false + }); + + // set environment suffix so these environments are loaded. + METADATA.envFileSuffix = METADATA.E2E ? 'e2e.prod' : 'prod'; + + return webpackMerge(commonConfig({ env: ENV, metadata: METADATA }), { + mode: 'production', - /** - * Developer tool to enhance debugging - * - * See: http://webpack.github.io/docs/configuration.html#devtool - * See: https://github.com/webpack/docs/wiki/build-performance#sourcemaps - */ devtool: 'source-map', /** * Options affecting the output of the compilation. * - * See: http://webpack.github.io/docs/configuration.html#output + * See: https://webpack.js.org/configuration/output/ */ output: { - /** * The output directory as absolute path (required). * - * See: http://webpack.github.io/docs/configuration.html#output-path + * See: https://webpack.js.org/configuration/output/#output-path */ path: helpers.root('dist'), @@ -71,7 +85,7 @@ module.exports = function (env) { * Specifies the name of each output file on disk. * IMPORTANT: You must not specify an absolute path here! * - * See: http://webpack.github.io/docs/configuration.html#output-filename + * See: https://webpack.js.org/configuration/output/#output-filename */ filename: '[name].[chunkhash].bundle.js', @@ -79,7 +93,7 @@ module.exports = function (env) { * The filename of the SourceMaps for the JavaScript files. * They are inside the output.path directory. * - * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename + * See: https://webpack.js.org/configuration/output/#output-sourcemapfilename */ sourceMapFilename: '[file].map', @@ -87,25 +101,19 @@ module.exports = function (env) { * The filename of non-entry chunks as relative path * inside the output.path directory. * - * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename + * See: https://webpack.js.org/configuration/output/#output-chunkfilename */ chunkFilename: '[name].[chunkhash].chunk.js' - }, module: { - rules: [ - /** * Extract CSS files from .src/styles directory to external CSS file */ { test: /\.css$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: 'css-loader' - }), + use: [MiniCssExtractPlugin.loader, 'css-loader'], include: [helpers.root('src', 'styles')] }, @@ -114,222 +122,50 @@ module.exports = function (env) { */ { test: /\.scss$/, - loader: ExtractTextPlugin.extract({ - fallback: 'style-loader', - use: 'css-loader!sass-loader' - }), + use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], include: [helpers.root('src', 'styles')] - }, - + } ] + }, + optimization: { + minimizer: [ + /** + * Plugin: UglifyJsPlugin + * Description: Minimize all JavaScript output of chunks. + * Loaders are switched into minimizing mode. + * + * See: https://webpack.js.org/plugins/uglifyjs-webpack-plugin/ + * + * NOTE: To debug prod builds uncomment //debug lines and comment //prod lines + */ + new UglifyJsPlugin({ + sourceMap: sourceMapEnabled, + parallel: true, + cache: helpers.root('webpack-cache/uglify-cache'), + uglifyOptions: getUglifyOptions(supportES2015, true) + }) + ], + splitChunks: { + chunks: 'all' + } }, /** * Add additional plugins to the compiler. * - * See: http://webpack.github.io/docs/configuration.html#plugins + * See: https://webpack.js.org/configuration/plugins/ */ plugins: [ - - /** - * Webpack plugin to optimize a JavaScript file for faster initial load - * by wrapping eagerly-invoked functions. - * - * See: https://github.com/vigneshshanmugam/optimize-js-plugin - */ - new OptimizeJsPlugin({ - sourceMap: false - }), - - /** - * Plugin: ExtractTextPlugin - * Description: Extracts imported CSS files into external stylesheet - * - * See: https://github.com/webpack/extract-text-webpack-plugin - */ - new ExtractTextPlugin('[name].[contenthash].css'), - - /** - * Plugin: DefinePlugin - * Description: Define free variables. - * Useful for having development builds with debug logging or adding global constants. - * - * Environment helpers - * - * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin - */ - // NOTE: when adding more properties make sure you include them in custom-typings.d.ts - new DefinePlugin({ - 'ENV': JSON.stringify(METADATA.ENV), - 'HMR': METADATA.HMR, - 'process.env': { - 'ENV': JSON.stringify(METADATA.ENV), - 'NODE_ENV': JSON.stringify(METADATA.ENV), - 'HMR': METADATA.HMR, - } - }), - - /** - * Plugin: UglifyJsPlugin - * Description: Minimize all JavaScript output of chunks. - * Loaders are switched into minimizing mode. - * - * See: https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin - * - * NOTE: To debug prod builds uncomment //debug lines and comment //prod lines - */ - new UglifyJsPlugin({ - // beautify: true, //debug - // mangle: false, //debug - // dead_code: false, //debug - // unused: false, //debug - // deadCode: false, //debug - // compress: { - // screw_ie8: true, - // keep_fnames: true, - // drop_debugger: false, - // dead_code: false, - // unused: false - // }, // debug - // comments: true, //debug - - - beautify: false, //prod - output: { - comments: false - }, //prod - mangle: { - screw_ie8: true - }, //prod - compress: { - screw_ie8: true, - warnings: false, - conditionals: true, - unused: true, - comparisons: true, - sequences: true, - dead_code: true, - evaluate: true, - if_return: true, - join_vars: true, - negate_iife: false // we need this for lazy v8 - }, - }), - - /** - * Plugin: NormalModuleReplacementPlugin - * Description: Replace resources that matches resourceRegExp with newResource - * - * See: http://webpack.github.io/docs/list-of-plugins.html#normalmodulereplacementplugin - */ - new NormalModuleReplacementPlugin( - /angular2-hmr/, - helpers.root('config/empty.js') - ), - - new NormalModuleReplacementPlugin( - /zone\.js(\\|\/)dist(\\|\/)long-stack-trace-zone/, - helpers.root('config/empty.js') - ), - - new HashedModuleIdsPlugin(), - - /** - * AoT - */ - /* - new NormalModuleReplacementPlugin( - /@angular(\\|\/)upgrade/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /@angular(\\|\/)compiler/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /@angular(\\|\/)platform-browser-dynamic/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /dom(\\|\/)debug(\\|\/)ng_probe/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /dom(\\|\/)debug(\\|\/)by/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /src(\\|\/)debug(\\|\/)debug_node/, - helpers.root('config/empty.js') - ), - new NormalModuleReplacementPlugin( - /src(\\|\/)debug(\\|\/)debug_renderer/, - helpers.root('config/empty.js') - ), - */ - - /** - * Plugin: CompressionPlugin - * Description: Prepares compressed versions of assets to serve - * them with Content-Encoding - * - * See: https://github.com/webpack/compression-webpack-plugin - */ - // install compression-webpack-plugin - // new CompressionPlugin({ - // regExp: /\.css$|\.html$|\.js$|\.map$/, - // threshold: 2 * 1024 - // }) - - /** - * Plugin LoaderOptionsPlugin (experimental) - * - * See: https://gist.github.com/sokra/27b24881210b56bbaff7 - */ - new LoaderOptionsPlugin({ - minimize: true, - debug: false, - options: { - - /** - * Html loader advanced options - * - * See: https://github.com/webpack/html-loader#advanced-options - */ - // TODO: Need to workaround Angular 2's html syntax => #id [bind] (event) *ngFor - htmlLoader: { - minimize: true, - removeAttributeQuotes: false, - caseSensitive: true, - customAttrSurround: [ - [/#/, /(?:)/], - [/\*/, /(?:)/], - [/\[?\(?/, /(?:)/] - ], - customAttrAssign: [/\)?\]?=/] - }, - - } - }), - - /** - * Plugin: BundleAnalyzerPlugin - * Description: Webpack plugin and CLI utility that represents - * bundle content as convenient interactive zoomable treemap - * - * `npm run build:prod -- --env.analyze` to use - * - * See: https://github.com/th0r/webpack-bundle-analyzer - */ - + new MiniCssExtractPlugin({ filename: '[name]-[hash].css', chunkFilename: '[name]-[chunkhash].css' }), + new HashedModuleIdsPlugin() ], /** * Include polyfills or mocks for various node stuff * Description: Node configuration * - * See: https://webpack.github.io/docs/configuration.html#node + * See: https://webpack.js.org/configuration/node/ */ node: { global: true, @@ -337,8 +173,8 @@ module.exports = function (env) { process: false, module: false, clearImmediate: false, - setImmediate: false + setImmediate: false, + fs: 'empty' } - }); -} +}; diff --git a/config/webpack.test.js b/config/webpack.test.js index 4a1b6c4d2f..6d08c7fd3d 100644 --- a/config/webpack.test.js +++ b/config/webpack.test.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ const helpers = require('./helpers'); @@ -15,16 +15,16 @@ const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin') /** * Webpack Constants */ -const ENV = process.env.ENV = process.env.NODE_ENV = 'test'; +const ENV = (process.env.ENV = process.env.NODE_ENV = 'test'); /** * Webpack configuration * - * See: http://webpack.github.io/docs/configuration.html#cli + * See: https://webpack.js.org/configuration/ */ -module.exports = function (options) { +module.exports = function(options) { return { - + mode: 'development', /** * Source map for Karma from the help of karma-sourcemap-loader & karma-webpack * @@ -36,14 +36,13 @@ module.exports = function (options) { /** * Options affecting the resolving of modules. * - * See: http://webpack.github.io/docs/configuration.html#resolve + * See: https://webpack.js.org/configuration/resolve/ */ resolve: { - /** * An array of extensions that should be used to resolve modules. * - * See: http://webpack.github.io/docs/configuration.html#resolve-extensions + * See: https://webpack.js.org/configuration/resolve/#resolve-extensions */ extensions: ['.ts', '.js'], @@ -51,21 +50,18 @@ module.exports = function (options) { * Make sure root is src */ modules: [helpers.root('src'), 'node_modules'] - }, /** * Options affecting the normal modules. * - * See: http://webpack.github.io/docs/configuration.html#module + * See: https://webpack.js.org/configuration/module/ * * 'use:' revered back to 'loader:' as a temp. workaround for #1188 - * See: https://github.com/AngularClass/angular-starter/issues/1188#issuecomment-262872034 + * See: https://github.com/gdi2290/angular-starter/issues/1188#issuecomment-262872034 */ module: { - rules: [ - /** * Source map loader support for *.js files * Extracts SourceMaps for source files that as added as sourceMappingURL comment. @@ -80,7 +76,6 @@ module.exports = function (options) { /** * These packages have problems with their sourcemaps */ - helpers.root('node_modules/rxjs'), helpers.root('node_modules/@angular') ] }, @@ -102,15 +97,13 @@ module.exports = function (options) { sourceMap: false, inlineSourceMap: true, compilerOptions: { - /** * Remove TypeScript helpers to be injected * below by DefinePlugin */ removeComments: true - } - }, + } }, 'angular2-template-loader' ], @@ -125,7 +118,7 @@ module.exports = function (options) { */ { test: /\.css$/, - loader: ['to-string-loader', 'css-loader'], + loader: ['to-string-loader', { loader: 'css-loader', options: { url: false } }], exclude: [helpers.root('src/index.html')] }, @@ -135,9 +128,9 @@ module.exports = function (options) { * See: https://github.com/webpack/raw-loader */ { - test: /\.scss$/, - loader: ['raw-loader', 'sass-loader'], - exclude: [helpers.root('src/index.html')] + test: /\.scss$/, + loader: ['raw-loader', 'sass-loader'], + exclude: [helpers.root('src/index.html')] }, /** @@ -163,22 +156,17 @@ module.exports = function (options) { test: /\.(js|ts)$/, loader: 'istanbul-instrumenter-loader', include: helpers.root('src'), - exclude: [ - /\.(e2e|spec)\.ts$/, - /node_modules/ - ] + exclude: [/\.(e2e|spec)\.ts$/, /node_modules/] } - ] }, /** * Add additional plugins to the compiler. * - * See: http://webpack.github.io/docs/configuration.html#plugins + * See: https://webpack.js.org/configuration/plugins/ */ plugins: [ - /** * Plugin: DefinePlugin * Description: Define free variables. @@ -186,17 +174,17 @@ module.exports = function (options) { * * Environment helpers * - * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin + * See: https://webpack.js.org/plugins/define-plugin/ * * NOTE: when adding more properties make sure you include them in custom-typings.d.ts */ new DefinePlugin({ - 'ENV': JSON.stringify(ENV), - 'HMR': false, + ENV: JSON.stringify(ENV), + HMR: false, 'process.env': { - 'ENV': JSON.stringify(ENV), - 'NODE_ENV': JSON.stringify(ENV), - 'HMR': false, + ENV: JSON.stringify(ENV), + NODE_ENV: JSON.stringify(ENV), + HMR: false } }), @@ -204,14 +192,14 @@ module.exports = function (options) { * Plugin: ContextReplacementPlugin * Description: Provides context to Angular's use of System.import * - * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin + * See: https://webpack.js.org/plugins/context-replacement-plugin/ * See: https://github.com/angular/angular/issues/11580 */ new ContextReplacementPlugin( /** * The (\\|\/) piece accounts for path separators in *nix and Windows */ - /angular(\\|\/)core(\\|\/)@angular/, + /\@angular(\\|\/)core(\\|\/)esm5/, helpers.root('src'), // location of your src { /** @@ -232,8 +220,7 @@ module.exports = function (options) { * legacy options go here */ } - }), - + }) ], /** @@ -249,16 +236,16 @@ module.exports = function (options) { * Include polyfills or mocks for various node stuff * Description: Node configuration * - * See: https://webpack.github.io/docs/configuration.html#node + * See: https://webpack.js.org/configuration/node/ */ node: { global: true, - process: false, crypto: 'empty', + process: false, module: false, clearImmediate: false, - setImmediate: false + setImmediate: false, + fs: 'empty' } - }; -} +}; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..7f61f3725c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3' +services: + angular-starter: + build: + context: . + dockerfile: Dockerfile-dev + container_name: angular-starter + networks: + - angular-starter + ports: + - '3000:3000' + environment: + - HOST=0.0.0.0 + command: npm run start +networks: + angular-starter: + driver: bridge diff --git a/karma.conf.js b/karma.conf.js index be68fa4c03..79ce74d23a 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ /** diff --git a/package.json b/package.json index 6d3899fe0a..40135678b8 100644 --- a/package.json +++ b/package.json @@ -1,42 +1,50 @@ { "name": "angular-starter", - "version": "6.0.0", - "description": "An Angular Webpack Starter kit featuring Angular (Router, Http, Forms, Services, Tests, E2E, Coverage), Karma, Protractor, Jasmine, Istanbul, TypeScript, and Webpack by AngularClass", + "version": "7.5.0", + "description": "An Angular Webpack Starter kit featuring Angular (Router, Http, Forms, Services, Tests, E2E, Coverage), Karma, Protractor, Jasmine, Istanbul, TypeScript, and Webpack by Tipe.io", "keywords": [ "angular", "angular2", "angular4", + "angular5", "webpack", - "typescript" + "typescript", + "tipe", + "tipe.io" ], - "author": "Patrick Stapleton ", - "homepage": "https://github.com/AngularClass/angular-starter", + "author": "Patrick Stapleton ", + "homepage": "https://github.com/gdi2290/angular-starter", "license": "MIT", "scripts": { - "build:aot:prod": "npm run clean:dist && npm run clean:aot && cross-env BUILD_AOT=1 npm run webpack -- --config config/webpack.prod.js --progress --profile --bail", + "build:aot:prod": "rimraf dist compiled && cross-env BUILD_AOT=1 SOURCE_MAP=0 npm run webpack -- --config config/webpack.prod.js --progress --profile --bail", "build:aot": "npm run build:aot:prod", - "build:dev": "npm run clean:dist && npm run webpack -- --config config/webpack.dev.js --progress --profile", - "build:docker": "npm run build:prod && docker build -t angular2-webpack-start:latest .", - "build:prod": "npm run clean:dist && npm run webpack -- --config config/webpack.prod.js --progress --profile --bail", + "build:aot:dev": "cross-env BUILD_AOT=1 npm run build:dev", + "build:dev": "rimraf dist && npm run webpack -- --config config/webpack.dev.js --mode development --progress --profile --trace-deprecation", + "build:docker": "npm run build:prod && docker build -t angular-webpack-starter:latest .", + "build:prod": "rimraf dist && npm run webpack -- --config config/webpack.prod.js --progress --profile --bail", "build": "npm run build:dev", - "ci:aot": "npm run lint && npm run test && npm run build:aot && npm run e2e", - "ci:jit": "npm run lint && npm run test && npm run build:prod && npm run e2e", - "ci:nobuild": "npm run lint && npm test && npm run e2e", - "ci:testall": "npm run lint && npm run test && npm run build:prod && npm run e2e && npm run build:aot && npm run e2e", - "ci:travis": "npm run lint && npm run test && npm run build:aot && npm run e2e:travis", + "ci:aot": "cross-env BUILD_E2E=1 npm run lint && npm run test:ci && npm run build:aot && npm run e2e", + "ci:jit": "cross-env BUILD_E2E=1 npm run lint && npm run test:ci && npm run build:prod && npm run e2e", + "ci:nobuild": "npm run lint && npm test:ci && npm run e2e", + "ci:testall": "cross-env BUILD_E2E=1 npm run lint && npm run test:ci && npm run build:prod && npm run e2e && npm run build:aot && npm run e2e", + "ci:travis": "cross-env BUILD_E2E=1 npm run lint && npm run test:ci && npm run build:aot && npm run e2e:travis", "ci": "npm run ci:testall", - "clean:dll": "npm run rimraf -- dll", + "clean:all": "npm run rimraf -- doc coverage dist compiled webpack-cache", + "clean:cache": "npm run rimraf -- webpack-cache", "clean:aot": "npm run rimraf -- compiled", "clean:dist": "npm run rimraf -- dist", "clean:install": "npm set progress=false && npm install", - "clean": "npm cache clean --force && npm run rimraf -- node_modules doc coverage dist compiled dll", + "clean": "npm cache verify && npm run rimraf -- node_modules doc coverage dist compiled webpack-cache", "docker": "docker", "docs": "npm run typedoc -- --options typedoc.json --exclude '**/*.spec.ts' ./src/", + "docs:compodoc": "compodoc -p tsconfig.json", + "docs:compodoc:serve": "compodoc -p tsconfig.json -s", + "docs:compodoc:serve:watch": "compodoc -p tsconfig.json -s -w", "e2e:live": "npm-run-all -p -r server:prod:ci protractor:live", "e2e:travis": "npm-run-all -p -r server:prod:ci protractor:delay", "e2e": "npm-run-all -p -r server:prod:ci protractor", - "github-deploy:dev": "npm run webpack -- --config config/webpack.github-deploy.js --progress --profile --env.githubDev", - "github-deploy:prod": "npm run webpack -- --config config/webpack.github-deploy.js --progress --profile --env.githubProd", + "github-deploy:dev": "npm run webpack -- --config config/webpack.github-deploy.js --mode development --progress --profile --env.githubDev", + "github-deploy:prod": "npm run webpack -- --config config/webpack.github-deploy.js --mode production --progress --profile --env.githubProd", "github-deploy": "npm run github-deploy:dev", "lint": "npm run tslint \"src/**/*.ts\"", "node": "node", @@ -48,121 +56,128 @@ "protractor:delay": "sleep 3 && npm run protractor", "protractor:live": "protractor --elementExplorer", "rimraf": "rimraf", - "server:dev:hmr": "npm run server:dev -- --inline --hot", + "server:dev:hmr": "npm run server:dev -- --hot --hotOnly", + "server:aot:dev": "cross-env BUILD_AOT=1 npm run server:dev", "server:dev": "npm run webpack-dev-server -- --config config/webpack.dev.js --open --progress --profile --watch --content-base src/", "server:prod": "http-server dist -c-1 --cors", "server:prod:ci": "http-server dist -p 3000 -c-1 --cors", "server": "npm run server:dev", + "start:prod:hmr": "cross-env ANGULAR_CONF_FILE=./config.prod.json npm run server:dev:hmr", "start:hmr": "npm run server:dev:hmr", "start": "npm run server:dev", - "test": "npm run lint && karma start", - "tslint": "tslint", + "start:aot": "npm run server:aot:dev", + "test": "karma start", + "test:sonar": "npm run lint && cross-env SONAR_QUBE=1 karma start", + "test:ci": "karma start --single-run --browsers ChromeTravisCi", + "tslint": "tslint \"src/**/*.ts\" --project tsconfig.json", "typedoc": "typedoc", "version": "npm run build", "watch:dev:hmr": "npm run watch:dev -- --hot", "watch:dev": "npm run build:dev -- --watch", + "watch:aot:dev": "npm run build:aot:dev -- --watch", "watch:prod": "npm run build:prod -- --watch", + "watch:aot:prod": "npm run build:aot:prod -- --watch", "watch:test": "npm run test -- --auto-watch --no-single-run", "watch": "npm run watch:dev", - "webdriver-manager": "webdriver-manager", - "webdriver:start": "npm run webdriver-manager start", - "webdriver:update": "webdriver-manager update", + "webdriver-manager": "node ./node_modules/protractor/bin/webdriver-manager", + "webdriver:start": "node ./node_modules/protractor/bin/webdriver-manager start", + "webdriver:update": "node ./node_modules/protractor/bin/webdriver-manager update", "webpack-dev-server": "node --max_old_space_size=4096 node_modules/webpack-dev-server/bin/webpack-dev-server.js", "webpack": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js" }, "dependencies": { - "@angular/animations": "~4.3.1", - "@angular/common": "~4.3.1", - "@angular/compiler": "~4.3.1", - "@angular/core": "~4.3.1", - "@angular/forms": "~4.3.1", - "@angular/http": "~4.3.1", - "@angular/platform-browser": "~4.3.1", - "@angular/platform-browser-dynamic": "~4.3.1", - "@angular/platform-server": "~4.3.1", - "@angular/router": "~4.3.1", - "@angularclass/hmr": "~1.2.2", - "@angularclass/hmr-loader": "^3.0.4", - "core-js": "^2.4.1", - "http-server": "^0.9.0", + "@angular/animations": "^6.0.6", + "@angular/common": "^6.0.6", + "@angular/compiler": "^6.0.6", + "@angular/core": "^6.0.6", + "@angular/forms": "^6.0.6", + "@angular/platform-browser": "^6.0.6", + "@angular/platform-browser-dynamic": "^6.0.6", + "@angular/platform-server": "^6.0.6", + "@angular/router": "^6.0.6", + "core-js": "^2.5.7", + "http-server": "^0.11.1", "ie-shim": "^0.1.0", - "reflect-metadata": "^0.1.10", - "rxjs": "~5.0.2", - "zone.js": "0.8.14" + "reflect-metadata": "^0.1.12", + "rxjs": "^6.2.1", + "zone.js": "^0.8.26" }, "devDependencies": { - "@angular/compiler-cli": "~4.3.1", - "@types/hammerjs": "^2.0.34", - "@types/jasmine": "2.5.45", - "@types/node": "^7.0.39", - "@types/source-map": "^0.5.0", - "@types/uglify-js": "^2.6.28", - "@types/webpack": "^2.2.16", - "add-asset-html-webpack-plugin": "^1.0.2", + "@angular-devkit/build-optimizer": "^0.6.8", + "@angular/cli": "^6.0.8", + "@angular/compiler-cli": "^6.0.6", + "@angular/language-service": "^6.0.6", + "@compodoc/compodoc": "^1.1.3", + "@ngtools/webpack": "^6.0.8", + "@types/hammerjs": "^2.0.35", + "@types/jasmine": "^2.8.8", + "@types/node": "^10.3.5", + "@types/uglify-js": "^3.0.2", + "@types/webpack": "^4.4.2", + "add-asset-html-webpack-plugin": "^2.1.3", "angular2-template-loader": "^0.6.2", - "assets-webpack-plugin": "^3.5.1", - "awesome-typescript-loader": "~3.1.2", - "codelyzer": "~2.1.1", - "copy-webpack-plugin": "^4.0.1", - "cross-env": "^5.0.0", - "css-loader": "^0.28.0", - "exports-loader": "^0.6.4", - "expose-loader": "^0.7.3", - "extract-text-webpack-plugin": "~2.1.0", - "file-loader": "^0.11.1", - "find-root": "^1.0.0", - "gh-pages": "^1.0.0", - "html-webpack-plugin": "^2.28.0", - "imports-loader": "^0.7.1", - "inline-manifest-webpack-plugin": "^3.0.1", - "istanbul-instrumenter-loader": "2.0.0", - "jasmine-core": "^2.5.2", - "karma": "^1.6.0", - "karma-chrome-launcher": "^2.0.0", - "karma-coverage": "^1.1.1", - "karma-jasmine": "^1.1.0", - "karma-mocha-reporter": "^2.2.3", - "karma-remap-coverage": "^0.1.4", + "assets-webpack-plugin": "^3.8.4", + "awesome-typescript-loader": "^5.2.0", + "codelyzer": "^4.3.0", + "copy-webpack-plugin": "^4.5.1", + "cross-env": "^5.2.0", + "css-loader": "^0.28.11", + "exports-loader": "^0.7.0", + "expose-loader": "^0.7.5", + "file-loader": "^1.1.11", + "find-root": "^1.1.0", + "gh-pages": "^1.2.0", + "html-webpack-plugin": "^3.2.0", + "imports-loader": "^0.8.0", + "istanbul-instrumenter-loader": "^3.0.1", + "jasmine-core": "^3.1.0", + "karma": "^2.0.4", + "karma-chrome-launcher": "^2.2.0", + "karma-coverage": "^1.1.2", + "karma-jasmine": "^1.1.2", + "karma-mocha-reporter": "^2.2.5", + "karma-remap-coverage": "^0.1.5", "karma-sourcemap-loader": "^0.3.7", - "karma-webpack": "^2.0.4", - "ng-router-loader": "^2.1.0", - "ngc-webpack": "^3.2.0", - "node-sass": "^4.5.2", - "npm-run-all": "^4.0.2", - "optimize-js-plugin": "0.0.4", - "parse5": "^3.0.2", - "preload-webpack-plugin": "^1.2.2", - "protractor": "^5.1.1", - "raw-loader": "0.5.1", - "rimraf": "~2.6.1", - "sass-loader": "^6.0.3", - "script-ext-html-webpack-plugin": "^1.8.5", - "source-map-loader": "^0.2.1", - "string-replace-loader": "~1.2.0", - "style-loader": "^0.18.1", + "karma-webpack": "^3.0.0", + "mini-css-extract-plugin": "^0.4.0", + "node-sass": "^4.9.0", + "npm-run-all": "^4.1.3", + "optimize-js-plugin": "^0.0.4", + "parse5": "^5.0.0", + "protractor": "^5.3.2", + "raw-loader": "^0.5.1", + "rimraf": "^2.6.2", + "rxjs-tslint": "^0.1.5", + "sass-loader": "^7.0.3", + "script-ext-html-webpack-plugin": "^2.0.1", + "source-map-loader": "^0.2.3", + "string-replace-loader": "^2.1.1", + "style-loader": "^0.21.0", "to-string-loader": "^1.1.5", - "ts-node": "^3.3.0", - "tslib": "^1.7.1", - "tslint": "~4.5.1", - "tslint-loader": "^3.5.2", - "typedoc": "^0.7.1", - "typescript": "~2.2.2", - "url-loader": "^0.5.8", - "webpack": "~2.6.1", - "webpack-dev-middleware": "^1.10.1", - "webpack-dev-server": "~2.4.2", - "webpack-dll-bundles-plugin": "^1.0.0-beta.5", - "webpack-merge": "~4.1.0" + "ts-node": "^7.0.0", + "tslib": "^1.9.3", + "tslint": "^5.10.0", + "tslint-loader": "^3.6.0", + "typedoc": "^0.11.1", + "typescript": "~2.7.2", + "uglifyjs-webpack-plugin": "^1.2.6", + "url-loader": "^1.0.1", + "webpack": "^4.12.0", + "webpack-cli": "^3.0.8", + "webpack-dev-middleware": "^3.1.3", + "webpack-dev-server": "^3.1.4", + "webpack-inline-manifest-plugin": "^4.0.1", + "webpack-merge": "^4.1.3" }, "repository": { "type": "git", - "url": "https://github.com/AngularClass/angular-starter.git" + "url": "https://github.com/gdi2290/angular-starter.git" }, "bugs": { - "url": "https://github.com/AngularClass/angular-starter/issues" + "url": "https://github.com/gdi2290/angular-starter/issues" }, "engines": { - "node": ">= 4.2.1", - "npm": ">= 3" + "node": ">= 8.0.0", + "npm": ">= 5" } } diff --git a/protractor.conf.js b/protractor.conf.js index 7fb872d47a..7e93e836e1 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ /** diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000000..6dbf1798c3 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,18 @@ +sonar.projectKey=angular:angular-starter +sonar.projectName=angular-starter +sonar.projectVersion=6.0.0 +sonar.sourceEncoding=UTF-8 +sonar.sources=src +sonar.exclusions=**/node_modules/**,**/*.spec.ts +sonar.tests=src/app +sonar.test.inclusions=**/*.spec.ts + +sonar.ts.tslint.configPath=tslint.json +sonar.ts.coverage.lcovReportPath=coverage/coverage.lcov +# if using local tslint then enable the line below +# sonar.ts.tslint.outputPath=reports/lint_issues.json +sonar.genericcoverage.unitTestReportPaths=reports/ut_report.xml + +# Change the host.url to point to the +# sonarcube server (default localhost) +sonar.host.url=http://localhost:9000 \ No newline at end of file diff --git a/src/app/+barrel/+child-barrel/child-barrel.component.ts b/src/app/+barrel/+child-barrel/child-barrel.component.ts index c4f5692a9a..ad41402046 100644 --- a/src/app/+barrel/+child-barrel/child-barrel.component.ts +++ b/src/app/+barrel/+child-barrel/child-barrel.component.ts @@ -13,7 +13,7 @@ console.log('`ChildBarrel` component loaded asynchronously'); @Component({ selector: 'child-barrel', template: ` -

Hello from Child Barrel

+

Hello from Child Barrel

`, }) export class ChildBarrelComponent implements OnInit { diff --git a/src/app/+detail/+child-detail/child-detail.component.ts b/src/app/+detail/+child-detail/child-detail.component.ts index d49111ee95..235f2911dc 100644 --- a/src/app/+detail/+child-detail/child-detail.component.ts +++ b/src/app/+detail/+child-detail/child-detail.component.ts @@ -13,7 +13,7 @@ console.log('`ChildDetail` component loaded asynchronously'); @Component({ selector: 'child-detail', template: ` -

Hello from Child Detail

+

Hello from Child Detail

`, }) export class ChildDetailComponent implements OnInit { diff --git a/src/app/+dev-module/dev-module.component.ts b/src/app/+dev-module/dev-module.component.ts new file mode 100644 index 0000000000..393f46d068 --- /dev/null +++ b/src/app/+dev-module/dev-module.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'dev-module', + template: ` +

Hello from DevModule Component

+ `, +}) +export class DevModuleComponent implements OnInit { + + public ngOnInit() { + console.log('hello `DevModule` component'); + } + +} diff --git a/src/app/+dev-module/dev-module.module.ts b/src/app/+dev-module/dev-module.module.ts new file mode 100644 index 0000000000..83f4b0cb15 --- /dev/null +++ b/src/app/+dev-module/dev-module.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; + +import { routes } from './dev-module.routes'; +import { DevModuleComponent } from './dev-module.component'; + +/* + Don't leave side-effects outside of classes so this will tree-shake nicely on prod + e.g. `console.log('something')` is a side effect. +*/ +@NgModule({ + declarations: [ DevModuleComponent ], + imports: [ + CommonModule, + RouterModule.forChild(routes), + ], +}) +export class DevModuleModule { + public static routes = routes; + constructor() { + console.log('`DevModuleModule` module initialized'); + } +} diff --git a/src/app/+dev-module/dev-module.routes.ts b/src/app/+dev-module/dev-module.routes.ts new file mode 100644 index 0000000000..dc7323b4f2 --- /dev/null +++ b/src/app/+dev-module/dev-module.routes.ts @@ -0,0 +1,5 @@ +import { DevModuleComponent } from './dev-module.component'; + +export const routes = [ + { path: 'dev-module', component: DevModuleComponent } +]; diff --git a/src/app/+dev-module/index.ts b/src/app/+dev-module/index.ts new file mode 100644 index 0000000000..47a5d082ae --- /dev/null +++ b/src/app/+dev-module/index.ts @@ -0,0 +1 @@ +export { DevModuleModule } from './dev-module.module'; diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index 169bbf040e..a7a6d13140 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -16,7 +16,7 @@ import { ActivatedRoute } from '@angular/router';

- patrick@AngularClass.com + patrick@tipe.io

this.localState = {{ localState | json }}
@@ -56,7 +56,7 @@ export class AboutComponent implements OnInit { */ setTimeout(() => { - System.import('../../assets/mock-data/mock-data.json') + import('../../assets/mock-data/mock-data.json') .then((json) => { console.log('async mockData', json); this.localState = json; diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index cc1b19b7de..866abe6b04 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -49,10 +49,10 @@ describe(`App`, () => { expect(comp).toBeDefined(); }); - it(`should be @AngularClass`, () => { - expect(comp.url).toEqual('https://twitter.com/AngularClass'); - expect(comp.angularclassLogo).toEqual('assets/img/angularclass-avatar.png'); - expect(comp.name).toEqual('Angular 2 Webpack Starter'); + it(`should be @TipeIO`, () => { + expect(comp.twitter).toEqual('https://twitter.com/gdi2290'); + expect(comp.tipe).toEqual('assets/img/tipe.png'); + expect(comp.name).toEqual('Angular Starter'); }); it('should log ngOnInit', () => { diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3e0c18bf3a..5963620b89 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,19 +1,18 @@ /** * Angular 2 decorators and services */ -import { - Component, - OnInit, - ViewEncapsulation -} from '@angular/core'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { environment } from 'environments/environment'; import { AppState } from './app.service'; +export const ROOT_SELECTOR = 'app'; + /** * App Component * Top Level Component */ @Component({ - selector: 'app', + selector: ROOT_SELECTOR, encapsulation: ViewEncapsulation.None, styleUrls: [ './app.component.css' @@ -40,6 +39,10 @@ import { AppState } from './app.service'; routerLinkActive="active" [routerLinkActiveOptions]= "{exact: true}"> About + + DevModule +
@@ -49,19 +52,21 @@ import { AppState } from './app.service';
this.appState.state = {{ appState.state | json }}
` }) export class AppComponent implements OnInit { - public angularclassLogo = 'assets/img/angularclass-avatar.png'; - public name = 'Angular 2 Webpack Starter'; - public url = 'https://twitter.com/AngularClass'; + public name = 'Angular Starter'; + public tipe = 'assets/img/tipe.png'; + public twitter = 'https://twitter.com/gdi2290'; + public url = 'https://tipe.io'; + public showDevModule: boolean = environment.showDevModule; constructor( public appState: AppState @@ -74,7 +79,7 @@ export class AppComponent implements OnInit { } /** - * Please review the https://github.com/AngularClass/angular2-examples/ repo for + * Please review the https://github.com/AngularClass/angular-examples/ repo for * more angular app examples that you may copy/paste * (The examples may not be updated as quickly. Please open an issue on github for us to update it) * For help or questions please contact us at @AngularClass on twitter diff --git a/src/app/app.e2e.ts b/src/app/app.e2e.ts index 6df018440e..368de16db5 100644 --- a/src/app/app.e2e.ts +++ b/src/app/app.e2e.ts @@ -8,26 +8,26 @@ describe('App', () => { }); it('should have a title', async () => { - let subject = await browser.getTitle(); - let result = 'Angular2 Webpack Starter by @gdi2290 from @AngularClass'; + const subject = await browser.getTitle(); + const result = 'Angular Starter by @gdi2290 from @TipeIO'; expect(subject).toEqual(result); }); it('should have header', async () => { - let subject = await element(by.css('h1')).isPresent(); - let result = true; + const subject = await element(by.css('h1')).isPresent(); + const result = true; expect(subject).toEqual(result); }); it('should have ', async () => { - let subject = await element(by.css('app home')).isPresent(); - let result = true; + const subject = await element(by.css('app home')).isPresent(); + const result = true; expect(subject).toEqual(result); }); it('should have buttons', async () => { - let subject = await element(by.css('button')).getText(); - let result = 'Submit Value'; + const subject = await element(by.css('button')).getText(); + const result = 'Submit Value'; expect(subject).toEqual(result); }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 0da2bdac15..7a537c880e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,24 +1,14 @@ +import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; -import { HttpModule } from '@angular/http'; -import { - NgModule, - ApplicationRef -} from '@angular/core'; -import { - removeNgStyles, - createNewHosts, - createInputTransfer -} from '@angularclass/hmr'; -import { - RouterModule, - PreloadAllModules -} from '@angular/router'; +import { HttpClientModule } from '@angular/common/http'; +import { RouterModule, PreloadAllModules } from '@angular/router'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; /* * Platform and Environment providers/directives/pipes */ -import { ENV_PROVIDERS } from './environment'; +import { environment } from 'environments/environment'; import { ROUTES } from './app.routes'; // App is our top level component import { AppComponent } from './app.component'; @@ -28,6 +18,7 @@ import { HomeComponent } from './home'; import { AboutComponent } from './about'; import { NoContentComponent } from './no-content'; import { XLargeDirective } from './home/x-large'; +import { DevModuleModule } from './+dev-module'; import '../styles/styles.scss'; import '../styles/headings.css'; @@ -38,11 +29,11 @@ const APP_PROVIDERS = [ AppState ]; -type StoreType = { - state: InternalStateType, - restoreInputValues: () => void, - disposeOldHosts: () => void -}; +interface StoreType { + state: InternalStateType; + restoreInputValues: () => void; + disposeOldHosts: () => void; +} /** * `AppModule` is the main entry point into Angular2's bootstraping process @@ -61,74 +52,27 @@ type StoreType = { */ imports: [ BrowserModule, + BrowserAnimationsModule, FormsModule, - HttpModule, - RouterModule.forRoot(ROUTES, { useHash: true, preloadingStrategy: PreloadAllModules }) + HttpClientModule, + RouterModule.forRoot(ROUTES, { + useHash: Boolean(history.pushState) === false, + preloadingStrategy: PreloadAllModules + }), + + /** + * This section will import the `DevModuleModule` only in certain build types. + * When the module is not imported it will get tree shaked. + * This is a simple example, a big app should probably implement some logic + */ + ...environment.showDevModule ? [ DevModuleModule ] : [], ], /** * Expose our Services and Providers into Angular's dependency injection. */ providers: [ - ENV_PROVIDERS, + environment.ENV_PROVIDERS, APP_PROVIDERS ] }) -export class AppModule { - - constructor( - public appRef: ApplicationRef, - public appState: AppState - ) {} - - public hmrOnInit(store: StoreType) { - if (!store || !store.state) { - return; - } - console.log('HMR store', JSON.stringify(store, null, 2)); - /** - * Set state - */ - this.appState._state = store.state; - /** - * Set input values - */ - if ('restoreInputValues' in store) { - let restoreInputValues = store.restoreInputValues; - setTimeout(restoreInputValues); - } - - this.appRef.tick(); - delete store.state; - delete store.restoreInputValues; - } - - public hmrOnDestroy(store: StoreType) { - const cmpLocation = this.appRef.components.map((cmp) => cmp.location.nativeElement); - /** - * Save state - */ - const state = this.appState._state; - store.state = state; - /** - * Recreate root elements - */ - store.disposeOldHosts = createNewHosts(cmpLocation); - /** - * Save input values - */ - store.restoreInputValues = createInputTransfer(); - /** - * Remove styles - */ - removeNgStyles(); - } - - public hmrAfterDestroy(store: StoreType) { - /** - * Display new elements - */ - store.disposeOldHosts(); - delete store.disposeOldHosts; - } - -} +export class AppModule {} diff --git a/src/app/app.resolver.ts b/src/app/app.resolver.ts index 45774b8d19..ca5bec8615 100644 --- a/src/app/app.resolver.ts +++ b/src/app/app.resolver.ts @@ -1,12 +1,11 @@ import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/observable/of'; +import { of } from 'rxjs'; @Injectable() export class DataResolver implements Resolve { public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - return Observable.of({ res: 'I am data'}); + return of({ res: 'I am data'}); } } diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index ce5792321a..39ca8dc2af 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -3,8 +3,6 @@ import { HomeComponent } from './home'; import { AboutComponent } from './about'; import { NoContentComponent } from './no-content'; -import { DataResolver } from './app.resolver'; - export const ROUTES: Routes = [ { path: '', component: HomeComponent }, { path: 'home', component: HomeComponent }, diff --git a/src/app/app.service.ts b/src/app/app.service.ts index a7eb880a49..ad84162ade 100644 --- a/src/app/app.service.ts +++ b/src/app/app.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; -export type InternalStateType = { - [key: string]: any -}; +export interface InternalStateType { + [key: string]: any; +} @Injectable() export class AppState { diff --git a/src/app/environment.ts b/src/app/environment.ts deleted file mode 100644 index 24690e2049..0000000000 --- a/src/app/environment.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Angular 2 - */ -import { - enableDebugTools, - disableDebugTools -} from '@angular/platform-browser'; -import { - ApplicationRef, - enableProdMode -} from '@angular/core'; -/** - * Environment Providers - */ -let PROVIDERS: any[] = [ - /** - * Common env directives - */ -]; - -/** - * Angular debug tools in the dev console - * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md - */ -let _decorateModuleRef = (value: T): T => { return value; }; - -if ('production' === ENV) { - enableProdMode(); - - /** - * Production - */ - _decorateModuleRef = (modRef: any) => { - disableDebugTools(); - - return modRef; - }; - - PROVIDERS = [ - ...PROVIDERS, - /** - * Custom providers in production. - */ - ]; - -} else { - - _decorateModuleRef = (modRef: any) => { - const appRef = modRef.injector.get(ApplicationRef); - const cmpRef = appRef.components[0]; - - enableDebugTools(cmpRef); - return modRef; - }; - - /** - * Development - */ - PROVIDERS = [ - ...PROVIDERS, - /** - * Custom providers in development. - */ - ]; - -} - -export const decorateModuleRef = _decorateModuleRef; - -export const ENV_PROVIDERS = [ - ...PROVIDERS -]; diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts index 163a0e452b..e3f71c5808 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/home/home.component.spec.ts @@ -3,15 +3,11 @@ import { inject, async, TestBed, - ComponentFixture + ComponentFixture, + getTestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; -import { - BaseRequestOptions, - ConnectionBackend, - Http -} from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; /** * Load the implementations that should be tested. @@ -23,6 +19,9 @@ import { Title } from './title'; describe(`Home`, () => { let comp: HomeComponent; let fixture: ComponentFixture; + let injector: TestBed; + let service: AppState; + let httpMock: HttpTestingController; /** * async beforeEach. @@ -31,24 +30,17 @@ describe(`Home`, () => { TestBed.configureTestingModule({ declarations: [HomeComponent], schemas: [NO_ERRORS_SCHEMA], - providers: [ - BaseRequestOptions, - MockBackend, - { - provide: Http, - useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => { - return new Http(backend, defaultOptions); - }, - deps: [MockBackend, BaseRequestOptions] - }, - AppState, - Title, - ] + imports: [HttpClientTestingModule], + providers: [AppState, Title] }) - /** - * Compile template and css. - */ - .compileComponents(); + + /** + * Compile template and css. + */ + .compileComponents(); + injector = getTestBed(); + service = injector.get(AppState); + httpMock = injector.get(HttpTestingController); })); /** diff --git a/src/app/home/home.e2e.ts b/src/app/home/home.e2e.ts index e9c433056c..d8dd92cd16 100644 --- a/src/app/home/home.e2e.ts +++ b/src/app/home/home.e2e.ts @@ -7,18 +7,19 @@ describe('Home', () => { /** * Change hash depending on router LocationStrategy. */ - await browser.get('/#/home'); + await browser.get('/'); + await element(by.linkText('Home')).click(); }); it('should have a title', async () => { - let subject = await browser.getTitle(); - let result = 'Angular2 Webpack Starter by @gdi2290 from @AngularClass'; + const subject = await browser.getTitle(); + const result = 'Angular Starter by @gdi2290 from @TipeIO'; expect(subject).toEqual(result); }); it('should have `your content here` x-large', async () => { - let subject = await element(by.css('[x-large]')).getText(); - let result = 'Your Content Here'; + const subject = await element(by.css('[x-large]')).getText(); + const result = 'Your Content Here'; expect(subject).toEqual(result); }); diff --git a/src/app/home/title/title.service.spec.ts b/src/app/home/title/title.service.spec.ts index 61ef537974..5e55da4800 100644 --- a/src/app/home/title/title.service.spec.ts +++ b/src/app/home/title/title.service.spec.ts @@ -1,42 +1,35 @@ import { inject, - TestBed + async, + TestBed, + ComponentFixture, + getTestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; -import { - BaseRequestOptions, - ConnectionBackend, - Http -} from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { Title } from './title.service'; describe('Title', () => { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - BaseRequestOptions, - MockBackend, - { - provide: Http, - useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => { - return new Http(backend, defaultOptions); - }, - deps: [MockBackend, BaseRequestOptions] - }, - Title - ]})); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [Title] + }); + }); - it('should have http', inject([ Title ], (title: Title) => { + it('should have http', inject([Title], (title: Title) => { expect(!!title.http).toEqual(true); })); - it('should get data from the server', inject([ Title ], (title: Title) => { + it('should get data from the server', inject([Title], (title: Title) => { spyOn(console, 'log'); expect(console.log).not.toHaveBeenCalled(); title.getData(); expect(console.log).toHaveBeenCalled(); - expect(title.getData()).toEqual({ value: 'AngularClass' }); + title.getData().subscribe( (result) => { + expect(result).toEqual({ value: 'AngularClass' }); + }); })); }); diff --git a/src/app/home/title/title.service.ts b/src/app/home/title/title.service.ts index 7f72b1d35f..ff3c41b68b 100644 --- a/src/app/home/title/title.service.ts +++ b/src/app/home/title/title.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; @Injectable() export class Title { @@ -7,18 +7,12 @@ export class Title { public value = 'Angular 2'; constructor( - public http: Http - ) {} + public http: HttpClient + ) { } public getData() { console.log('Title#getData(): Get Data'); - /** - * return this.http.get('/assets/data.json') - * .map(res => res.json()); - */ - return { - value: 'AngularClass' - }; + return this.http.get('/assets/data.json'); } } diff --git a/src/app/home/x-large/x-large.directive.spec.ts b/src/app/home/x-large/x-large.directive.spec.ts index 7e6e8ce6d3..fe7312bf7e 100644 --- a/src/app/home/x-large/x-large.directive.spec.ts +++ b/src/app/home/x-large/x-large.directive.spec.ts @@ -1,13 +1,15 @@ +import { By } from '@angular/platform-browser'; import { - fakeAsync, inject, + fakeAsync, tick, - TestBed + async, + TestBed, + ComponentFixture, + getTestBed } from '@angular/core/testing'; import { Component } from '@angular/core'; -import { BaseRequestOptions, Http } from '@angular/http'; -import { By } from '@angular/platform-browser'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; /** * Load the implementations that should be tested. diff --git a/src/app/home/x-large/x-large.directive.ts b/src/app/home/x-large/x-large.directive.ts index 3f71424256..83adab9f12 100644 --- a/src/app/home/x-large/x-large.directive.ts +++ b/src/app/home/x-large/x-large.directive.ts @@ -9,6 +9,7 @@ import { * XLarge is a simple directive to show how one is made */ @Directive({ + // tslint:disable-next-line:directive-selector selector: '[x-large]' // using [ ] means selecting attributes }) export class XLargeDirective { diff --git a/src/assets/img/angular-logo.png b/src/assets/img/angular.png similarity index 100% rename from src/assets/img/angular-logo.png rename to src/assets/img/angular.png diff --git a/src/assets/img/angularclass-avatar.png b/src/assets/img/angularclass-avatar.png deleted file mode 100644 index 3f5f8bc022..0000000000 Binary files a/src/assets/img/angularclass-avatar.png and /dev/null differ diff --git a/src/assets/img/angularclass-logo.png b/src/assets/img/angularclass-logo.png deleted file mode 100644 index 4342316a84..0000000000 Binary files a/src/assets/img/angularclass-logo.png and /dev/null differ diff --git a/src/assets/img/tipe.png b/src/assets/img/tipe.png new file mode 100644 index 0000000000..4939f07061 Binary files /dev/null and b/src/assets/img/tipe.png differ diff --git a/src/custom-typings.d.ts b/src/custom-typings.d.ts index 621a416b4a..6a9f6d22af 100644 --- a/src/custom-typings.d.ts +++ b/src/custom-typings.d.ts @@ -60,6 +60,16 @@ declare module 'modern-lru' { declare var ENV: string; declare var HMR: boolean; declare var System: SystemJS; +// declare const FIREBASE_CONFIG: FirebaseConfig; + +interface FirebaseConfig { + apiKey: string; + authDomain: string; + databaseURL: string; + projectId: string; + storageBucket: string; + messagingSenderId: string; +} interface SystemJS { import: (path?: string) => Promise; @@ -70,6 +80,7 @@ interface GlobalEnvironment { HMR: boolean; SystemJS: SystemJS; System: SystemJS; + // FIREBASE_CONFIG: FirebaseConfig; } interface Es6PromiseLoader { diff --git a/src/environments/environment.e2e.prod.ts b/src/environments/environment.e2e.prod.ts new file mode 100644 index 0000000000..2aaa245f23 --- /dev/null +++ b/src/environments/environment.e2e.prod.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +import { enableProdMode, NgModuleRef } from '@angular/core'; +import { disableDebugTools } from '@angular/platform-browser'; +import { Environment } from './model'; + +enableProdMode(); + +// export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG; + +export const environment: Environment = { + production: true, + showDevModule: true, + + /** Angular debug tools in the dev console + * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md + * @param modRef + * @return {any} + */ + decorateModuleRef(modRef: NgModuleRef) { + disableDebugTools(); + return modRef; + }, + ENV_PROVIDERS: [ + + ] +}; diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000000..7c04dd7b9a --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +import { enableProdMode, NgModuleRef } from '@angular/core'; +import { disableDebugTools } from '@angular/platform-browser'; +import { Environment } from './model'; + +enableProdMode(); + +// export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG; + +export const environment: Environment = { + production: true, + showDevModule: false, + + /** Angular debug tools in the dev console + * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md + * @param modRef + * @return {any} + */ + decorateModuleRef(modRef: NgModuleRef) { + disableDebugTools(); + return modRef; + }, + ENV_PROVIDERS: [ + + ] +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000000..995732f715 --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ + +import { ApplicationRef, NgModuleRef } from '@angular/core'; +import { enableDebugTools } from '@angular/platform-browser'; +import { Environment } from './model'; + +Error.stackTraceLimit = Infinity; +require('zone.js/dist/long-stack-trace-zone'); + +// export const ENV_FIREBASE_CONFIG: any = FIREBASE_CONFIG; + +export const environment: Environment = { + production: false, + + showDevModule: true, + + /** Angular debug tools in the dev console + * https://github.com/angular/angular/blob/86405345b781a9dc2438c0fbe3e9409245647019/TOOLS_JS.md + * @param modRef + * @return {any} + */ + decorateModuleRef(modRef: NgModuleRef) { + const appRef = modRef.injector.get(ApplicationRef); + const cmpRef = appRef.components[0]; + + let _ng = (window).ng; + enableDebugTools(cmpRef); + (window).ng.probe = _ng.probe; + (window).ng.coreTokens = _ng.coreTokens; + return modRef; + }, + ENV_PROVIDERS: [ + + ] +}; + diff --git a/src/environments/model.ts b/src/environments/model.ts new file mode 100644 index 0000000000..2c46f0e828 --- /dev/null +++ b/src/environments/model.ts @@ -0,0 +1,8 @@ +import { NgModuleRef } from '@angular/core'; + +export interface Environment { + production: boolean; + ENV_PROVIDERS: any; + showDevModule: boolean; + decorateModuleRef(modRef: NgModuleRef): NgModuleRef; +} diff --git a/src/index.html b/src/index.html index 66180fbd77..502936bd3c 100644 --- a/src/index.html +++ b/src/index.html @@ -5,7 +5,7 @@ <%= htmlWebpackPlugin.options.title %> - + <% if (webpackConfig.htmlElements.headTags) { %> @@ -13,7 +13,6 @@ <%= webpackConfig.htmlElements.headTags %> <% } %> - <%= htmlWebpackPlugin.files.webpackManifest %> <% if (htmlWebpackPlugin.options.metadata.isDevServer && htmlWebpackPlugin.options.metadata.HMR !== true) { %> @@ -21,13 +20,16 @@ <% } %> - - - + + <% if (htmlWebpackPlugin.options.gtmKey) { %> + + + + <% } %> @@ -36,6 +38,12 @@ +<% if (htmlWebpackPlugin.options.gtmKey) { %> + + + +<% } %> Loading... diff --git a/src/main.browser.aot.ts b/src/main.browser.aot.ts deleted file mode 100644 index 90915ffdd7..0000000000 --- a/src/main.browser.aot.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Angular bootstrapping - */ -import { platformBrowser } from '@angular/platform-browser'; -import { decorateModuleRef } from './app/environment'; -/** - * App Module - * our top level module that holds all of our components. - */ -import { AppModuleNgFactory } from '../compiled/src/app/app.module.ngfactory'; -/** - * Bootstrap our Angular app with a top level NgModule. - */ -export function main(): Promise { - return platformBrowser() - .bootstrapModuleFactory(AppModuleNgFactory) - .then(decorateModuleRef) - .catch((err) => console.error(err)); -} - -switch (document.readyState) { - case 'loading': - document.addEventListener('DOMContentLoaded', _domReadyHandler, false); - break; - case 'interactive': - case 'complete': - default: - main(); -} - -function _domReadyHandler() { - document.removeEventListener('DOMContentLoaded', _domReadyHandler, false); - main(); -} diff --git a/src/main.browser.ts b/src/main.browser.ts index a47977425a..170a5c4ad0 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -2,22 +2,44 @@ * Angular bootstrapping */ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { decorateModuleRef } from './app/environment'; +import { environment } from 'environments/environment'; +import { NgModuleRef } from '@angular/core'; /** * App Module * our top level module that holds all of our components */ import { AppModule } from './app'; +import { ROOT_SELECTOR } from './app/app.component'; /** * Bootstrap our Angular app with a top level NgModule */ export function main(): Promise { - return platformBrowserDynamic() - .bootstrapModule(AppModule) - .then(decorateModuleRef) - .catch((err) => console.error(err)); + let modulePromise: Promise> = null; + + if (module['hot']) { + module['hot'].accept(); + module['hot'].dispose(() => { + // Before restarting the app, we create a new root element and dispose the old one + const oldRootElem = document.querySelector(ROOT_SELECTOR); + const newRootElem = document.createElement(ROOT_SELECTOR); + oldRootElem!.parentNode!.insertBefore(newRootElem, oldRootElem); + if (modulePromise) { + modulePromise.then((appModule) => { + appModule.destroy(); + if (oldRootElem !== null) { + oldRootElem!.parentNode!.removeChild(oldRootElem); + } + return appModule; + }); + } + }); + } + + modulePromise = platformBrowserDynamic().bootstrapModule(AppModule); + + return modulePromise.then(environment.decorateModuleRef).catch((err) => console.error(err)); } /** diff --git a/src/polyfills.browser.ts b/src/polyfills.browser.ts index 3e46fc4f99..77edce8764 100644 --- a/src/polyfills.browser.ts +++ b/src/polyfills.browser.ts @@ -1,50 +1,79 @@ -// TODO(gdi2290): switch to DLLs - /** - * Polyfills + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html */ -/* import 'ie-shim'; *//* Internet Explorer 9 support */ +/*************************************************************************************************** + * BROWSER POLYFILLS + */ -/* import 'core-js/es6'; */ +// Internet Explorer 9 support +// import 'ie-shim'; -/** - * Added parts of es6 which are necessary for your project or your browser support requirements. - */ -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/set'; -import 'core-js/es6/weak-map'; -import 'core-js/es6/weak-set'; -import 'core-js/es6/typed'; +// IE9, IE10 and IE11 requires all of the following polyfills. +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +// Evergreen browsers require these. import 'core-js/es6/reflect'; +import 'core-js/es7/reflect'; + /** - * See issue https://github.com/AngularClass/angular-starter/issues/709 + * Required to support Web Animations `@angular/animation`. + * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation */ - /* import 'core-js/es6/promise'; */ -import 'core-js/es7/reflect'; +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/*************************************************************************************************** + * Zone JS is required by Angular itself. + */ import 'zone.js/dist/zone'; +// import 'zone.js/dist/long-stack-trace-zone' // async stack traces with zone.js included for dev + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ +/** + * Date, currency, decimal and percent pipes. + * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 + */ +// import 'intl'; // Run `npm install --save intl`. +/** + * Need to import at least one locale-data with intl. + */ +// import 'intl/locale-data/jsonp/en'; if ('production' === ENV) { // Production } else { // Development - Error.stackTraceLimit = Infinity; - - /* tslint:disable no-var-requires */ - require('zone.js/dist/long-stack-trace-zone'); - } diff --git a/tsconfig.json b/tsconfig.json index d642cec8ce..171a02263b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "dom", "es6" ], - "baseUrl": ".", + "baseUrl": "./src", "paths": { "@angular/*": ["node_modules/@angular/*"] }, @@ -26,7 +26,6 @@ "hammerjs", "jasmine", "node", - "source-map", "uglify-js", "webpack" ] diff --git a/tsconfig.webpack.json b/tsconfig.webpack.json index 814868a691..0a428609e5 100644 --- a/tsconfig.webpack.json +++ b/tsconfig.webpack.json @@ -1,13 +1,12 @@ { "compilerOptions": { "target": "es5", - "module": "es2015", + "module": "esnext", "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "sourceMap": true, - "noEmit": true, "noEmitHelpers": true, "importHelpers": true, "strictNullChecks": false, @@ -15,9 +14,9 @@ "es2015", "dom" ], - "baseUrl": ".", + "baseUrl": "./src", "paths": { - "@angular/*": ["node_modules/@angular/*"] + "@angular/*": ["../node_modules/@angular/*"] }, "typeRoots": [ "node_modules/@types" @@ -33,12 +32,7 @@ "src/**/*.spec.ts", "src/**/*.e2e.ts" ], - "awesomeTypescriptLoaderOptions": { - "forkChecker": true, - "useWebpackText": true - }, "angularCompilerOptions": { - "genDir": "./compiled", "skipMetadataEmit": true }, "compileOnSave": false, diff --git a/tslint.json b/tslint.json index e1f9814cdd..3164caafb0 100644 --- a/tslint.json +++ b/tslint.json @@ -2,33 +2,34 @@ "extends": [ "tslint:recommended" ], + "linterOptions": { + "exclude": [ + "src/*.d.ts" + ] + }, "rulesDirectory": [ - "node_modules/codelyzer" + "node_modules/codelyzer", + "node_modules/rxjs-tslint" ], "rules": { - // Custom "trailing-comma": [false, {"multiline": "always", "singleline": "never"}], "interface-name": [false, "always-prefix"], - // Angular 2 "component-class-suffix": true, // "component-selector": [true, "element", "my", "kebab-case"], "directive-class-suffix": true, // "directive-selector": [true, "attribute", "my", "camelCase"], "import-destructuring-spacing": true, - "invoke-injectable": true, - "no-access-missing-member": true, "no-attribute-parameter-decorator": true, "no-forward-ref": true, "no-input-rename": true, "no-output-rename": true, + "only-arrow-functions": false, "pipe-naming": [true, "camelCase", "my"], - "templates-use-public": true, "use-host-property-decorator": true, "use-input-property-decorator": true, "use-life-cycle-interface": true, "use-output-property-decorator": true, "use-pipe-transform-interface": true, - // General "no-console": [true, "time", "timeEnd", @@ -53,6 +54,10 @@ "allow-pascal-case", "ban-keywords", "check-format" - ] + ], + "rxjs-collapse-imports": true, + "rxjs-pipeable-operators-only": true, + "rxjs-no-static-observable-methods": true, + "rxjs-proper-imports": true } -} \ No newline at end of file +} diff --git a/webpack.config.js b/webpack.config.js index 620bbd1f26..810987f306 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,5 @@ /** - * @author: @AngularClass + * @author: tipe.io */ /**