Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement --base-href argument #1506

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
@@ -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`
6 changes: 4 additions & 2 deletions addon/ng2/commands/build.ts
Original file line number Diff line number Diff line change
@@ -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({
@@ -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) {
46 changes: 32 additions & 14 deletions addon/ng2/commands/github-pages-deploy.ts
Original file line number Diff line number Diff line change
@@ -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'],
@@ -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 = {
@@ -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({
@@ -123,7 +146,7 @@ module.exports = Command.extend({
.then(createGitHubRepoIfNeeded)
.then(checkoutGhPages)
.then(copyFiles)
.then(updateBaseHref)
.then(createNotFoundPage)
.then(addAndCommit)
.then(returnStartingBranch)
.then(pushToGitRepo)
@@ -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() {
11 changes: 10 additions & 1 deletion addon/ng2/models/webpack-build-common.ts
Original file line number Diff line number Diff line change
@@ -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);
@@ -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.
10 changes: 8 additions & 2 deletions addon/ng2/models/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -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);

7 changes: 4 additions & 3 deletions addon/ng2/tasks/build-webpack-watch.ts
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange, that nobody has noticed yet that build-webpack and build-webpack-watch tasks uses the options interface from serve command. 😕

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like one of those things that slipped :/


const project = this.cliProject;

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

7 changes: 4 additions & 3 deletions addon/ng2/tasks/build-webpack.ts
Original file line number Diff line number Diff line change
@@ -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/';

@@ -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;

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

const webpackCompiler = webpack(config);
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