diff --git a/README.md b/README.md
index ef4b9014c3c8..ec65bdf2c5e6 100644
--- a/README.md
+++ b/README.md
@@ -141,6 +141,20 @@ You can also add your own env files other than `dev` and `prod` by creating a
`src/app/environments/environment.{NAME}.ts` and use them by using the `--env=NAME`
flag on the build/serve commands.
+### Base tag handling in index.html
+
+You can modify base tag (``) in your index.html by using `--base-href your-url` option. It's useful when building or serving for different environments.
+
+```bash
+# Sets base tag href to /myUrl/ in your index.html
+ng build --base-href /myUrl/
+ng serve --base-href /myUrl/
+
+# Does nothing
+ng build --base-href
+ng serve --base-href
+```
+
### Bundling
Builds created with the `-prod` flag via `ng build -prod` or `ng serve -prod` bundle
diff --git a/addon/ng2/commands/build.ts b/addon/ng2/commands/build.ts
index 935102fa8484..a3749004dd3c 100644
--- a/addon/ng2/commands/build.ts
+++ b/addon/ng2/commands/build.ts
@@ -9,6 +9,7 @@ interface BuildOptions {
watch?: boolean;
watcher?: string;
supressSizes: boolean;
+ baseHref?: string;
}
module.exports = Command.extend({
@@ -22,7 +23,8 @@ module.exports = Command.extend({
{ name: 'output-path', type: 'Path', default: 'dist/', aliases: ['o'] },
{ name: 'watch', type: Boolean, default: false, aliases: ['w'] },
{ name: 'watcher', type: String },
- { name: 'suppress-sizes', type: Boolean, default: false }
+ { name: 'suppress-sizes', type: Boolean, default: false },
+ { name: 'base-href', type: String, default: null },
],
run: function (commandOptions: BuildOptions) {
@@ -43,7 +45,8 @@ module.exports = Command.extend({
ui: ui,
outputPath: commandOptions.outputPath,
target: commandOptions.target,
- environment: commandOptions.environment
+ environment: commandOptions.environment,
+ baseHref: commandOptions.baseHref
}) :
new WebpackBuild({
cliProject: project,
@@ -51,6 +54,7 @@ module.exports = Command.extend({
outputPath: commandOptions.outputPath,
target: commandOptions.target,
environment: commandOptions.environment,
+ baseHref: commandOptions.baseHref
});
return buildTask.run(commandOptions);
diff --git a/addon/ng2/commands/serve.ts b/addon/ng2/commands/serve.ts
index 7e048aff2a39..7ea94b23e3f9 100644
--- a/addon/ng2/commands/serve.ts
+++ b/addon/ng2/commands/serve.ts
@@ -28,6 +28,7 @@ export interface ServeTaskOptions {
ssl?: boolean;
sslKey?: string;
sslCert?: string;
+ baseHref?: string;
}
module.exports = Command.extend({
@@ -51,7 +52,8 @@ module.exports = Command.extend({
{ name: 'output-path', type: 'Path', default: 'dist/', aliases: ['op', 'out'] },
{ name: 'ssl', type: Boolean, default: false },
{ name: 'ssl-key', type: String, default: 'ssl/server.key' },
- { name: 'ssl-cert', type: String, default: 'ssl/server.crt' }
+ { name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
+ { name: 'base-href', type: String, default: null },
],
run: function(commandOptions: ServeTaskOptions) {
@@ -85,6 +87,7 @@ module.exports = Command.extend({
ui: this.ui,
analytics: this.analytics,
project: this.project,
+ baseHref: commandOptions.baseHref
});
return serve.run(commandOptions);
diff --git a/addon/ng2/models/webpack-build-common.ts b/addon/ng2/models/webpack-build-common.ts
index edb412f710b1..66e3640d204f 100644
--- a/addon/ng2/models/webpack-build-common.ts
+++ b/addon/ng2/models/webpack-build-common.ts
@@ -4,8 +4,9 @@ import * as HtmlWebpackPlugin from 'html-webpack-plugin';
import * as webpack from 'webpack';
import { ForkCheckerPlugin } from 'awesome-typescript-loader';
import { CliConfig } from './config';
+import { BaseHrefWebpackPlugin } from '../utilities/base-href-webpack-plugin';
-export function getWebpackCommonConfig(projectRoot: string, sourceDir: string) {
+export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, baseHref: string) {
return {
devtool: 'inline-source-map',
resolve: {
@@ -64,6 +65,9 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string) {
template: path.resolve(projectRoot, `./${sourceDir}/index.html`),
chunksSortMode: 'dependency'
}),
+ new BaseHrefWebpackPlugin({
+ baseHref: baseHref
+ }),
new webpack.optimize.CommonsChunkPlugin({
name: ['polyfills']
}),
diff --git a/addon/ng2/models/webpack-config.ts b/addon/ng2/models/webpack-config.ts
index a215217c0afa..8aa148b8dd83 100644
--- a/addon/ng2/models/webpack-config.ts
+++ b/addon/ng2/models/webpack-config.ts
@@ -23,12 +23,12 @@ export class NgCliWebpackConfig {
private webpackMobileConfigPartial: any;
private webpackMobileProdConfigPartial: any;
- constructor(public ngCliProject: any, public target: string, public environment: string) {
+ constructor(public ngCliProject: any, public target: string, public environment: string, public baseHref: string) {
const sourceDir = CliConfig.fromProject().defaults.sourceDir;
const environmentPath = `./${sourceDir}/app/environments/environment.${environment}.ts`;
- this.webpackBaseConfig = getWebpackCommonConfig(this.ngCliProject.root, sourceDir);
+ this.webpackBaseConfig = getWebpackCommonConfig(this.ngCliProject.root, sourceDir, baseHref);
this.webpackDevConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, sourceDir);
this.webpackProdConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, sourceDir);
diff --git a/addon/ng2/tasks/build-webpack-watch.ts b/addon/ng2/tasks/build-webpack-watch.ts
index 7e6514c6513f..e0144395d80b 100644
--- a/addon/ng2/tasks/build-webpack-watch.ts
+++ b/addon/ng2/tasks/build-webpack-watch.ts
@@ -16,7 +16,7 @@ module.exports = Task.extend({
rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath));
- const config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment).config;
+ const config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment, runTaskOptions.baseHref).config;
const webpackCompiler = webpack(config);
webpackCompiler.apply(new ProgressPlugin({
diff --git a/addon/ng2/tasks/build-webpack.ts b/addon/ng2/tasks/build-webpack.ts
index 8d98d76a6812..aef91f480d31 100644
--- a/addon/ng2/tasks/build-webpack.ts
+++ b/addon/ng2/tasks/build-webpack.ts
@@ -11,12 +11,12 @@ let lastHash: any = null;
module.exports = Task.extend({
// Options: String outputPath
- run: function(runTaskOptions: ServeTaskOptions) {
+ run: function (runTaskOptions: ServeTaskOptions) {
var project = this.cliProject;
rimraf.sync(path.resolve(project.root, runTaskOptions.outputPath));
- var config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment).config;
+ var config = new NgCliWebpackConfig(project, runTaskOptions.target, runTaskOptions.environment, runTaskOptions.baseHref).config;
const webpackCompiler = webpack(config);
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
diff --git a/addon/ng2/tasks/serve-webpack.ts b/addon/ng2/tasks/serve-webpack.ts
index 60589e48c011..a798e4773fc6 100644
--- a/addon/ng2/tasks/serve-webpack.ts
+++ b/addon/ng2/tasks/serve-webpack.ts
@@ -15,7 +15,7 @@ module.exports = Task.extend({
let lastHash = null;
let webpackCompiler: any;
- var config: NgCliWebpackConfig = new NgCliWebpackConfig(this.project, commandOptions.target, commandOptions.environment).config;
+ var config: NgCliWebpackConfig = new NgCliWebpackConfig(this.project, commandOptions.target, commandOptions.environment, commandOptions.baseHref).config;
// This allows for live reload of page when changes are made to repo.
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
config.entry.main.unshift(`webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/`);
diff --git a/addon/ng2/utilities/base-href-webpack-plugin.ts b/addon/ng2/utilities/base-href-webpack-plugin.ts
new file mode 100644
index 000000000000..a5f5ae1556cb
--- /dev/null
+++ b/addon/ng2/utilities/base-href-webpack-plugin.ts
@@ -0,0 +1,32 @@
+interface BaseHrefWebpackPluginOptions {
+ baseHref: string;
+}
+
+export class BaseHrefWebpackPlugin {
+ constructor(private options: BaseHrefWebpackPluginOptions) { }
+
+ apply(compiler): void {
+ // Ignore if baseHref is not passed
+ if (!this.options.baseHref) {
+ return;
+ }
+
+ compiler.plugin('compilation', (compilation) => {
+ compilation.plugin('html-webpack-plugin-before-html-processing', (htmlPluginData, callback) => {
+ // Check if base tag already exists
+ const baseTagRegex = //i;
+ const baseTagMatches = htmlPluginData.html.match(baseTagRegex);
+ if (!baseTagMatches) {
+ // Insert it in top of the head if not exist
+ htmlPluginData.html = htmlPluginData.html.replace(//i, '$&' + ``);
+ } else {
+ // Replace only href attribute if exists
+ const modifiedBaseTag = baseTagMatches[0].replace(/href="\S+"/i, `href="${this.options.baseHref}"`);
+ htmlPluginData.html = htmlPluginData.html.replace(baseTagRegex, modifiedBaseTag);
+ }
+
+ callback(null, htmlPluginData);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/tests/acceptance/base-href.spec.js b/tests/acceptance/base-href.spec.js
new file mode 100644
index 000000000000..5cb6eca6de9d
--- /dev/null
+++ b/tests/acceptance/base-href.spec.js
@@ -0,0 +1,50 @@
+/*eslint-disable no-console */
+'use strict';
+
+var expect = require('chai').expect;
+var BaseHrefWebpackPlugin = require('../../addon/ng2/utilities/base-href-webpack-plugin').BaseHrefWebpackPlugin;
+
+function mockCompiler(indexHtml, callback) {
+ return {
+ plugin: function (event, compilerCallback) {
+ var compilation = {
+ plugin: function (hook, compilationCallback) {
+ var htmlPluginData = {
+ html: indexHtml
+ };
+ compilationCallback(htmlPluginData, callback);
+ }
+ };
+ compilerCallback(compilation);
+ }
+ };
+}
+
+describe.only('base href webpack plugin', function () {
+ it('should do nothing when baseHref is null', function () {
+ var plugin = new BaseHrefWebpackPlugin({ baseHref: null });
+
+ var compiler = mockCompiler('', function (x, htmlPluginData) {
+ expect(htmlPluginData.html).to.equal('');
+ });
+ plugin.apply(compiler);
+ });
+
+ it('should insert base tag when not exist', function () {
+ var plugin = new BaseHrefWebpackPlugin({ baseHref: '/' });
+
+ var compiler = mockCompiler('', function (x, htmlPluginData) {
+ expect(htmlPluginData.html).to.equal('');
+ });
+ plugin.apply(compiler);
+ });
+
+ it('should replace href attribute when base tag already exists', function () {
+ var plugin = new BaseHrefWebpackPlugin({ baseHref: '/myUrl/' });
+
+ var compiler = mockCompiler('', function (x, htmlPluginData) {
+ expect(htmlPluginData.html).to.equal('');
+ });
+ plugin.apply(compiler);
+ });
+});
diff --git a/tests/e2e/e2e_workflow.spec.js b/tests/e2e/e2e_workflow.spec.js
index 052efd377a21..ad836cabf1ac 100644
--- a/tests/e2e/e2e_workflow.spec.js
+++ b/tests/e2e/e2e_workflow.spec.js
@@ -125,6 +125,20 @@ describe('Basic end-to-end Workflow', function () {
});
});
+ it('Supports base tag modifications via `ng build --base-href`', function() {
+ this.timeout(420000);
+
+ // Check base tag before building with --base-href tag
+ const indexHtmlBefore = fs.readFileSync(path.join(process.cwd(), 'dist/index.html'), 'utf-8');
+ expect(indexHtmlBefore).to.match(/