Skip to content

Commit

Permalink
feat(build): implement --base-href argument
Browse files Browse the repository at this point in the history
Implement --base-href argument for build and github-pages:deploy commands

Closes #1064
Closes #1506
  • Loading branch information
dzonatan authored and filipesilva committed Aug 26, 2016
1 parent f03f275 commit 74b29b3
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 51 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The generated project has dependencies that require **Node 4.x.x and NPM 3.x.x**
* [Generating a Route](#generating-a-route)
* [Creating a Build](#creating-a-build)
* [Build Targets and Environment Files](#build-targets-and-environment-files)
* [Base tag handling in index.html](#base-tag-handling-in-indexhtml)
* [Adding extra files to the build](#adding-extra-files-to-the-build)
* [Running Unit Tests](#running-unit-tests)
* [Running End-to-End Tests](#running-end-to-end-tests)
Expand Down Expand Up @@ -152,6 +153,16 @@ You can also add your own env files other than `dev` and `prod` by doing the fol
- add `{ NAME: 'src/environments/environment.NAME.ts' }` to the the `apps[0].environments` object in `angular-cli.json`
- use them by using the `--env=NAME` flag on the build/serve commands.

### Base tag handling in index.html

When building you can modify base tag (`<base href="/">`) in your index.html with `--base-href your-url` option.

```bash
# Sets base tag href to /myUrl/ in your index.html
ng build --base-href /myUrl/
ng build --bh /myUrl/
```

### Bundling

All builds make use of bundling, and using the `--prod` flag in `ng build --prod`
Expand Down
6 changes: 4 additions & 2 deletions addon/ng2/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import * as Command from 'ember-cli/lib/models/command';
import * as WebpackBuild from '../tasks/build-webpack';
import * as WebpackBuildWatch from '../tasks/build-webpack-watch';

interface BuildOptions {
export interface BuildOptions {
target?: string;
environment?: string;
outputPath?: string;
watch?: boolean;
watcher?: string;
supressSizes: boolean;
baseHref?: string;
}

module.exports = Command.extend({
Expand All @@ -27,7 +28,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, aliases: ['bh'] },
],

run: function (commandOptions: BuildOptions) {
Expand Down
46 changes: 32 additions & 14 deletions addon/ng2/commands/github-pages-deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,20 @@ import * as CreateGithubRepo from '../tasks/create-github-repo';
import { CliConfig } from '../models/config';
import { oneLine } from 'common-tags';

const fsReadFile = Promise.denodeify(fs.readFile);
const fsWriteFile = Promise.denodeify(fs.writeFile);
const fsReadDir = Promise.denodeify(fs.readdir);
const fsCopy = Promise.denodeify(fse.copy);

interface GithubPagesDeployOptions {
message?: string;
target?: string;
environment?: string;
userPage?: boolean;
skipBuild?: boolean;
ghToken?: string;
ghUsername?: string;
baseHref?: string;
}

module.exports = Command.extend({
name: 'github-pages:deploy',
aliases: ['gh-pages:deploy'],
Expand Down Expand Up @@ -61,9 +70,14 @@ module.exports = Command.extend({
type: String,
default: '',
description: 'Github username'
}, {
name: 'base-href',
type: String,
default: null,
aliases: ['bh']
}],

run: function(options, rawArgs) {
run: function(options: GithubPagesDeployOptions, rawArgs) {
const ui = this.ui;
const root = this.project.root;
const execOptions = {
Expand Down Expand Up @@ -99,10 +113,19 @@ module.exports = Command.extend({
outputPath: outDir
});

/**
* BaseHref tag setting logic:
* First, use --base-href flag value if provided.
* Else if --user-page is true, then keep baseHref default as declared in index.html.
* Otherwise auto-replace with `/${projectName}/`.
*/
const baseHref = options.baseHref || (options.userPage ? null : `/${projectName}/`);

const buildOptions = {
target: options.target,
environment: options.environment,
outputPath: outDir
outputPath: outDir,
baseHref: baseHref,
};

const createGithubRepoTask = new CreateGithubRepo({
Expand All @@ -123,7 +146,7 @@ module.exports = Command.extend({
.then(createGitHubRepoIfNeeded)
.then(checkoutGhPages)
.then(copyFiles)
.then(updateBaseHref)
.then(createNotFoundPage)
.then(addAndCommit)
.then(returnStartingBranch)
.then(pushToGitRepo)
Expand Down Expand Up @@ -191,15 +214,10 @@ module.exports = Command.extend({
})));
}

function updateBaseHref() {
if (options.userPage) { return Promise.resolve(); }
let indexHtml = path.join(root, 'index.html');
return fsReadFile(indexHtml, 'utf8')
.then((data) => data.replace(/<base href="\/">/g, `<base href="/${projectName}/">`))
.then((data) => {
fsWriteFile(indexHtml, data, 'utf8');
fsWriteFile(path.join(root, '404.html'), data, 'utf8');
});
function createNotFoundPage() {
const indexHtml = path.join(root, 'index.html');
const notFoundPage = path.join(root, '404.html');
return fsCopy(indexHtml, notFoundPage);
}

function addAndCommit() {
Expand Down
11 changes: 10 additions & 1 deletion addon/ng2/models/webpack-build-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import * as webpack from 'webpack';
import * as atl from 'awesome-typescript-loader';

import { findLazyModules } from './find-lazy-modules';
import { BaseHrefWebpackPlugin } from '../utilities/base-href-webpack-plugin';

export function getWebpackCommonConfig(projectRoot: string, environment: string, appConfig: any) {
export function getWebpackCommonConfig(
projectRoot: string,
environment: string,
appConfig: any,
baseHref: string
) {

const appRoot = path.resolve(projectRoot, appConfig.root);
const appMain = path.resolve(appRoot, appConfig.main);
Expand Down Expand Up @@ -118,6 +124,9 @@ export function getWebpackCommonConfig(projectRoot: string, environment: string,
template: path.resolve(appRoot, appConfig.index),
chunksSortMode: 'dependency'
}),
new BaseHrefWebpackPlugin({
baseHref: baseHref
}),
new webpack.NormalModuleReplacementPlugin(
// This plugin is responsible for swapping the environment files.
// Since it takes a RegExp as first parameter, we need to escape the path.
Expand Down
10 changes: 8 additions & 2 deletions addon/ng2/models/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,20 @@ export class NgCliWebpackConfig {
public ngCliProject: any,
public target: string,
public environment: string,
outputDir?: string
outputDir?: string,
baseHref?: string
) {
const config: CliConfig = CliConfig.fromProject();
const appConfig = config.config.apps[0];

appConfig.outDir = outputDir || appConfig.outDir;

this.baseConfig = getWebpackCommonConfig(this.ngCliProject.root, environment, appConfig);
this.baseConfig = getWebpackCommonConfig(
this.ngCliProject.root,
environment,
appConfig,
baseHref
);
this.devConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, appConfig);
this.prodConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, appConfig);

Expand Down
7 changes: 4 additions & 3 deletions addon/ng2/tasks/build-webpack-watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import * as webpack from 'webpack';
import * as ProgressPlugin from 'webpack/lib/ProgressPlugin';
import { NgCliWebpackConfig } from '../models/webpack-config';
import { webpackOutputOptions } from '../models/';
import { ServeTaskOptions } from '../commands/serve';
import { BuildOptions } from '../commands/build';

let lastHash: any = null;

module.exports = Task.extend({
run: function(runTaskOptions: ServeTaskOptions) {
run: function(runTaskOptions: BuildOptions) {

const project = this.cliProject;

Expand All @@ -20,7 +20,8 @@ module.exports = Task.extend({
project,
runTaskOptions.target,
runTaskOptions.environment,
runTaskOptions.outputPath
runTaskOptions.outputPath,
runTaskOptions.baseHref
).config;
const webpackCompiler = webpack(config);

Expand Down
7 changes: 4 additions & 3 deletions addon/ng2/tasks/build-webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as rimraf from 'rimraf';
import * as path from 'path';
import * as Task from 'ember-cli/lib/models/task';
import * as webpack from 'webpack';
import { ServeTaskOptions } from '../commands/serve';
import { BuildOptions } from '../commands/build';
import { NgCliWebpackConfig } from '../models/webpack-config';
import { webpackOutputOptions } from '../models/';

Expand All @@ -11,7 +11,7 @@ let lastHash: any = null;

module.exports = Task.extend({
// Options: String outputPath
run: function(runTaskOptions: ServeTaskOptions) {
run: function (runTaskOptions: BuildOptions) {

const project = this.cliProject;

Expand All @@ -20,7 +20,8 @@ module.exports = Task.extend({
project,
runTaskOptions.target,
runTaskOptions.environment,
runTaskOptions.outputPath
runTaskOptions.outputPath,
runTaskOptions.baseHref
).config;

const webpackCompiler = webpack(config);
Expand Down
39 changes: 39 additions & 0 deletions addon/ng2/utilities/base-href-webpack-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
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 = /<base.*?>/i;
const baseTagMatches = htmlPluginData.html.match(baseTagRegex);
if (!baseTagMatches) {
// Insert it in top of the head if not exist
htmlPluginData.html = htmlPluginData.html.replace(
/<head>/i, '$&' + `<base href="${this.options.baseHref}">`
);
} 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);
}
);
});
}
}
50 changes: 50 additions & 0 deletions tests/acceptance/base-href-webpack-plugin.spec.js
Original file line number Diff line number Diff line change
@@ -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('base href webpack plugin', function () {
it('should do nothing when baseHref is null', function () {
var plugin = new BaseHrefWebpackPlugin({ baseHref: null });

var compiler = mockCompiler('<body><head></head></body>', function (x, htmlPluginData) {
expect(htmlPluginData.html).to.equal('<body><head></head></body>');
});
plugin.apply(compiler);
});

it('should insert base tag when not exist', function () {
var plugin = new BaseHrefWebpackPlugin({ baseHref: '/' });

var compiler = mockCompiler('<body><head></head></body>', function (x, htmlPluginData) {
expect(htmlPluginData.html).to.equal('<body><head><base href="/"></head></body>');
});
plugin.apply(compiler);
});

it('should replace href attribute when base tag already exists', function () {
var plugin = new BaseHrefWebpackPlugin({ baseHref: '/myUrl/' });

var compiler = mockCompiler('<body><head><base href="/" target="_blank"></head></body>', function (x, htmlPluginData) {
expect(htmlPluginData.html).to.equal('<body><head><base href="/myUrl/" target="_blank"></head></body>');
});
plugin.apply(compiler);
});
});
Loading

0 comments on commit 74b29b3

Please sign in to comment.