Skip to content

Commit

Permalink
feat(@angular/cli): add option to set dev server's base serve path
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Lyding authored and Brocco committed Aug 17, 2017
1 parent 6b115dc commit 2c6dde1
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
6 changes: 6 additions & 0 deletions packages/@angular/cli/commands/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ServeTaskOptions extends BuildOptions {
sslCert?: string;
open?: boolean;
hmr?: boolean;
servePath?: string;
}

// Expose options unrelated to live-reload to other commands that need to run serve
Expand Down Expand Up @@ -96,6 +97,11 @@ export const baseServeCommandOptions: any = overrideOptions([
default: false,
description: 'Don\'t verify connected clients are part of allowed hosts.',
},
{
name: 'serve-path',
type: String,
description: 'The pathname where the app will be served.'
},
{
name: 'hmr',
type: Boolean,
Expand Down
66 changes: 56 additions & 10 deletions packages/@angular/cli/tasks/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,39 @@ const SilentError = require('silent-error');
const opn = require('opn');
const yellow = require('chalk').yellow;

function findDefaultServePath(baseHref: string, deployUrl: string): string | null {
if (!baseHref && !deployUrl) {
return '';
}

if (/^(\w+:)?\/\//.test(baseHref) || /^(\w+:)?\/\//.test(deployUrl)) {
// If baseHref or deployUrl is absolute, unsupported by ng serve
return null;
}

// normalize baseHref
// for ng serve the starting base is always `/` so a relative
// and root relative value are identical
const baseHrefParts = (baseHref || '')
.split('/')
.filter(part => part !== '');
if (baseHref && !baseHref.endsWith('/')) {
baseHrefParts.pop();
}
const normalizedBaseHref = baseHrefParts.length === 0 ? '/' : `/${baseHrefParts.join('/')}/`;

if (deployUrl && deployUrl[0] === '/') {
if (baseHref && baseHref[0] === '/' && normalizedBaseHref !== deployUrl) {
// If baseHref and deployUrl are root relative and not equivalent, unsupported by ng serve
return null;
}
return deployUrl;
}

// Join together baseHref and deployUrl
return `${normalizedBaseHref}${deployUrl || ''}`;
}

export default Task.extend({
run: function (serveTaskOptions: ServeTaskOptions, rebuildDoneCb: any) {
const ui = this.ui;
Expand Down Expand Up @@ -156,10 +189,29 @@ export default Task.extend({
}
}

let servePath = serveTaskOptions.servePath;
if (!servePath && servePath !== '') {
const defaultServePath =
findDefaultServePath(serveTaskOptions.baseHref, serveTaskOptions.deployUrl);
if (defaultServePath == null) {
ui.writeLine(oneLine`
${chalk.yellow('WARNING')} --deploy-url and/or --base-href contain
unsupported values for ng serve. Default serve path of '/' used.
Use --serve-path to override.
`);
}
servePath = defaultServePath || '';
}
if (servePath.endsWith('/')) {
servePath = servePath.substr(0, servePath.length - 1);
}
if (!servePath.startsWith('/')) {
servePath = `/${servePath}`;
}
const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = {
headers: { 'Access-Control-Allow-Origin': '*' },
historyApiFallback: {
index: `/${appConfig.index}`,
index: `${servePath}/${appConfig.index}`,
disableDotRule: true,
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
},
Expand All @@ -177,7 +229,8 @@ export default Task.extend({
},
contentBase: false,
public: serveTaskOptions.publicHost,
disableHostCheck: serveTaskOptions.disableHostCheck
disableHostCheck: serveTaskOptions.disableHostCheck,
publicPath: servePath
};

if (sslKey != null && sslCert != null) {
Expand All @@ -187,13 +240,6 @@ export default Task.extend({

webpackDevServerConfiguration.hot = serveTaskOptions.hmr;

// set publicPath property to be sent on webpack server config
if (serveTaskOptions.deployUrl) {
webpackDevServerConfiguration.publicPath = serveTaskOptions.deployUrl;
(webpackDevServerConfiguration.historyApiFallback as any).index =
serveTaskOptions.deployUrl + `/${appConfig.index}`;
}

if (serveTaskOptions.target === 'production') {
ui.writeLine(chalk.red(stripIndents`
****************************************************************************************
Expand All @@ -208,7 +254,7 @@ export default Task.extend({
ui.writeLine(chalk.green(oneLine`
**
NG Live Development Server is listening on ${serveTaskOptions.host}:${serveTaskOptions.port},
open your browser on ${serverAddress}
open your browser on ${serverAddress}${servePath}
**
`));

Expand Down
29 changes: 29 additions & 0 deletions tests/e2e/tests/commands/serve/serve-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { request } from '../../../utils/http';
import { killAllProcesses } from '../../../utils/process';
import { ngServe } from '../../../utils/project';

export default function () {
return Promise.resolve()
.then(() => ngServe('--serve-path', 'test/'))
.then(() => request('http://localhost:4200/test'))
.then(body => {
if (!body.match(/<app-root><\/app-root>/)) {
throw new Error('Response does not match expected value.');
}
})
.then(() => request('http://localhost:4200/test/abc'))
.then(body => {
if (!body.match(/<app-root><\/app-root>/)) {
throw new Error('Response does not match expected value.');
}
})
.then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; })
.then(() => ngServe('--base-href', 'test/'))
.then(() => request('http://localhost:4200/test'))
.then(body => {
if (!body.match(/<app-root><\/app-root>/)) {
throw new Error('Response does not match expected value.');
}
})
.then(() => killAllProcesses(), (err) => { killAllProcesses(); throw err; });
}

0 comments on commit 2c6dde1

Please sign in to comment.