diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.hmr.ts b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.hmr.ts new file mode 100644 index 000000000000..09d59cef5098 --- /dev/null +++ b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.hmr.ts @@ -0,0 +1,9 @@ +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `.angular-cli.json`. + +export const environment = { + production: false,<% if (hmr) { %> + hmr: true,<% } %> +}; diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.prod.ts b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.prod.ts index 3612073bc31c..8fc1198310c6 100644 --- a/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.prod.ts +++ b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.prod.ts @@ -1,3 +1,4 @@ export const environment = { - production: true + production: true,<% if (hmr) { %> + hmr: false,<% } %> }; diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.ts b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.ts index b7f639aecac5..1d6cfc5e69ee 100644 --- a/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.ts +++ b/packages/@angular/cli/blueprints/ng/files/__path__/environments/environment.ts @@ -4,5 +4,6 @@ // The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { - production: false + production: false,<% if (hmr) { %> + hmr: false,<% } %> }; diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/hmr.ts b/packages/@angular/cli/blueprints/ng/files/__path__/hmr.ts new file mode 100644 index 000000000000..544da20c17df --- /dev/null +++ b/packages/@angular/cli/blueprints/ng/files/__path__/hmr.ts @@ -0,0 +1,15 @@ +import { NgModuleRef, ApplicationRef } from '@angular/core'; +import { createNewHosts } from '@angularclass/hmr'; + +export const hmrBootstrap = (module: any, bootstrap: () => Promise>) => { + let ngModule: NgModuleRef; + module.hot.accept(); + bootstrap().then(mod => ngModule = mod); + module.hot.dispose(() => { + let appRef: ApplicationRef = ngModule.injector.get(ApplicationRef); + let elements = appRef.components.map(c => c.location.nativeElement); + let makeVisible = createNewHosts(elements); + ngModule.destroy(); + makeVisible(); + }); +}; diff --git a/packages/@angular/cli/blueprints/ng/files/__path__/main.ts b/packages/@angular/cli/blueprints/ng/files/__path__/main.ts index a9ca1caf8cee..8a28d562760c 100644 --- a/packages/@angular/cli/blueprints/ng/files/__path__/main.ts +++ b/packages/@angular/cli/blueprints/ng/files/__path__/main.ts @@ -4,8 +4,23 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; -if (environment.production) { +<% if (hmr) { %>import { hmrBootstrap } from './hmr'; + +<% } %>if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +<% if (!hmr) { %>platformBrowserDynamic().bootstrapModule(AppModule);<% } %><% if (hmr) { %>const bootstrap = () => { + return platformBrowserDynamic().bootstrapModule(AppModule); +}; + + if (environment.hmr) { + if (module[ 'hot' ]) { + hmrBootstrap(module, bootstrap); + } else { + console.error('HMR is not enabled for webpack-dev-server!'); + console.log('Are you using the --hmr flag for ng serve?'); + } + } else { + bootstrap(); + }<% } %> diff --git a/packages/@angular/cli/blueprints/ng/files/angular-cli.json b/packages/@angular/cli/blueprints/ng/files/angular-cli.json index 93a828e33062..cf76e382aa43 100644 --- a/packages/@angular/cli/blueprints/ng/files/angular-cli.json +++ b/packages/@angular/cli/blueprints/ng/files/angular-cli.json @@ -24,7 +24,8 @@ "scripts": [], "environmentSource": "environments/environment.ts", "environments": { - "dev": "environments/environment.ts", + "dev": "environments/environment.ts",<% if (hmr) { %> + "hmr": "environments/environment.hmr.ts",<% } %> "prod": "environments/environment.prod.ts" } } diff --git a/packages/@angular/cli/blueprints/ng/files/package.json b/packages/@angular/cli/blueprints/ng/files/package.json index fc2f3609964d..522a7d33059d 100644 --- a/packages/@angular/cli/blueprints/ng/files/package.json +++ b/packages/@angular/cli/blueprints/ng/files/package.json @@ -4,7 +4,8 @@ "license": "MIT", "scripts": { "ng": "ng", - "start": "ng serve", + "start": "ng serve",<% if (hmr) { %> + "hmr": "ng serve --hmr -e=hmr",<% } %> "build": "ng build", "test": "ng test", "lint": "ng lint", @@ -26,7 +27,8 @@ }, "devDependencies": { "@angular/cli": "<%= version %>", - "@angular/compiler-cli": "<%= ng4 ? '>=4.0.0-beta <5.0.0' : '^2.4.0' %>", + "@angular/compiler-cli": "<%= ng4 ? '>=4.0.0-beta <5.0.0' : '^2.4.0' %>",<% if (hmr) { %> + "@angularclass/hmr": "^1.2.2",<% } %> "@types/jasmine": "2.5.38", "@types/node": "~6.0.60", "codelyzer": "~2.0.0", diff --git a/packages/@angular/cli/blueprints/ng/index.ts b/packages/@angular/cli/blueprints/ng/index.ts index 4de8a42cf01d..f2b76f2c3cec 100644 --- a/packages/@angular/cli/blueprints/ng/index.ts +++ b/packages/@angular/cli/blueprints/ng/index.ts @@ -15,7 +15,8 @@ export default Blueprint.extend({ { name: 'routing', type: Boolean, default: false }, { name: 'inline-style', type: Boolean, default: false, aliases: ['is'] }, { name: 'inline-template', type: Boolean, default: false, aliases: ['it'] }, - { name: 'skip-git', type: Boolean, default: false, aliases: ['sg'] } + { name: 'skip-git', type: Boolean, default: false, aliases: ['sg'] }, + { name: 'hmr', type: Boolean, default: false } ], beforeInstall: function(options: any) { @@ -54,6 +55,7 @@ export default Blueprint.extend({ routing: options.routing, inlineStyle: options.inlineStyle, inlineTemplate: options.inlineTemplate, + hmr: options.hmr, ng4: options.ng4, tests: this.tests }; @@ -71,6 +73,10 @@ export default Blueprint.extend({ if (this.options && this.options.inlineStyle) { fileList = fileList.filter(p => p.indexOf('app.component.__styleext__') < 0); } + if (this.options && !this.options.hmr) { + fileList = fileList.filter(p => p.indexOf('environment.hmr.ts') < 0); + fileList = fileList.filter(p => p.indexOf('hmr.ts') < 0); + } if (this.options && this.options.skipGit) { fileList = fileList.filter(p => p.indexOf('gitignore') < 0); } diff --git a/packages/@angular/cli/commands/init.ts b/packages/@angular/cli/commands/init.ts index e8cb2979d996..9dc9fa293f90 100644 --- a/packages/@angular/cli/commands/init.ts +++ b/packages/@angular/cli/commands/init.ts @@ -20,7 +20,8 @@ const InitCommand: any = Command.extend({ { name: 'prefix', type: String, default: 'app', aliases: ['p'] }, { name: 'routing', type: Boolean, default: false }, { name: 'inline-style', type: Boolean, default: false, aliases: ['is'] }, - { name: 'inline-template', type: Boolean, default: false, aliases: ['it'] } + { name: 'inline-template', type: Boolean, default: false, aliases: ['it'] }, + { name: 'hmr', type: Boolean, default: false } ], anonymousOptions: [''], diff --git a/packages/@angular/cli/commands/new.ts b/packages/@angular/cli/commands/new.ts index 0fd2945821e8..78f5b4295249 100644 --- a/packages/@angular/cli/commands/new.ts +++ b/packages/@angular/cli/commands/new.ts @@ -119,7 +119,13 @@ const NewCommand = Command.extend({ default: false, aliases: ['it'], description: 'Should have an inline template.' - } + }, + { + name: 'hmr', + type: Boolean, + default: false, + description: 'Should enable hot module replacement.' + } ], isProject: function (projectPath: string) { diff --git a/packages/@angular/cli/tasks/init.ts b/packages/@angular/cli/tasks/init.ts index e2ba1c2c96eb..c9bd8948fce5 100644 --- a/packages/@angular/cli/tasks/init.ts +++ b/packages/@angular/cli/tasks/init.ts @@ -74,6 +74,7 @@ export default Task.extend({ routing: commandOptions.routing, inlineStyle: commandOptions.inlineStyle, inlineTemplate: commandOptions.inlineTemplate, + hmr: commandOptions.hmr, ignoredUpdateFiles: ['favicon.ico'], ng4: commandOptions.ng4, skipGit: commandOptions.skipGit, diff --git a/packages/@angular/cli/tasks/serve.ts b/packages/@angular/cli/tasks/serve.ts index a455ef8e2afd..cbe426f1022f 100644 --- a/packages/@angular/cli/tasks/serve.ts +++ b/packages/@angular/cli/tasks/serve.ts @@ -64,15 +64,9 @@ export default Task.extend({ `webpack-dev-server/client?${clientAddress}` ]; if (serveTaskOptions.hmr) { - const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html'; ui.writeLine(oneLine` ${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server. `); - ui.writeLine(' The project will still live reload when HMR is enabled,'); - ui.writeLine(' but to take advantage of HMR additional application code is required'); - ui.writeLine(' (not included in an Angular CLI project by default).'); - ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`); - ui.writeLine(' for information on working with HMR for Webpack.'); entryPoints.push('webpack/hot/dev-server'); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); webpackConfig.plugins.push(new webpack.NamedModulesPlugin());