Angular-cli: Angular CLI 6.0.0 --base-href, --deploy-url no longer works as expected

Created on 8 May 2018  路  19Comments  路  Source: angular/angular-cli

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[X] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
[ ] Other... Please describe:

Current behavior


After upgrading to Angular 6 the --base-href and --deploy-url no longer work as expected.

I am running ng build --base-href /virtualDir --deploy-url /virtualDir --prod command to build the app which used to work in angular 5.2 without a problem. Now it tries to grab runtime.js, styles.js, main.js and polyfill.js without adding / after the base-href so it just ends up getting a 404 error.

Used to be: https://mydomain.com/virtualDir/styles.08dcsetc.css
Now it is: https://mydomain.com/virtualDirstyles.08dcsetc.css

Notice the missing / between virtualDir and styles.08dcsetc.css

If i add the / to the end of base-href and deploy-url, the main files get loaded but then every call to an API fails because and extra / gets added to every request.

The api calls look like this:
https://mydomain.com/virtualDir//api//someEndpoint

Expected behavior


Should load all required files from https://mydomain.com/virtualDir/

Minimal reproduction of the problem with instructions


Not sure how to reproduce this on stackblitz.com.

But this is the simples one i can come up with.

  • Create virtual directory on your server
  • Have a node instance running in that directory with following setup
const express = require('express');
const api = require('./api');
const path = require('path');

const app = express();
const port = process.env.PORT || 3000;
const virtualDirPath = process.env.virtualDirPath || '';

// static files (all angular stuff)
app.use(virtualDirPath, express.static(path.join(__dirname, 'dist')));
// Handles all api routes
app.use(`${virtualDirPath}/api`, api);

app.listen(port, () => {
  console.log(`listening on ${port}`);
});
  • Build and deploy angular app to said virtual directory with --base-href and --deploy-url set to your virtual dir
  • Try to run it.

What is the motivation / use case for changing the behavior?


Would like to be able to run multiple apps on same domain within virtual directories.

Environment


Angular version: 6.0.0
Angular CLI 6.0.0
Node: 8.11.1


Browser:
- [X] Chrome (desktop) version XX
- [X] Chrome (Android) version XX
- [X] Chrome (iOS) version XX
- [X] Firefox version XX
- [X] Safari (desktop) version XX
- [X] Safari (iOS) version XX
- [X] IE version XX
- [X] Edge version XX

For Tooling issues:
- Node version: 8.11.1  
- Platform:  Mac 

Others:

Most helpful comment

I added "baseHref": "/dir" to projects.Angular6.architect.build.options within a newly created Angular6 project's angular.json.

The option is ignored and the site is still accessing everything under root.

If you run ng serve --base-href=/dir, it used to show the message

open your browser on http://localhost:4200/dir/

But now, when running ng serve with the above configuration changes, the dir/ part is simply missing.

(As a side note, I feel a massive lack of documentation for the new build/configuration system, let alone migration documentation.)

All 19 comments

I think now some configuration must be done in the angular.json file and not the npm command anymore. (not sure).

I added "baseHref": "/dir" to projects.Angular6.architect.build.options within a newly created Angular6 project's angular.json.

The option is ignored and the site is still accessing everything under root.

If you run ng serve --base-href=/dir, it used to show the message

open your browser on http://localhost:4200/dir/

But now, when running ng serve with the above configuration changes, the dir/ part is simply missing.

(As a side note, I feel a massive lack of documentation for the new build/configuration system, let alone migration documentation.)

Yeah the docs aren't so great, the random changes are annoying. Hopefully it will get sorted out some time soon.

I was able to get it to work by adding / to the end of --deploy-url but not adding it to base-href.

This is what it looks like in package json now: ng build --base-href /virtualDir --deploy-url /virtualDir/ --prod

I also build without any base-href or deploy-url on local, just have a interceptor that removes the / from window.location.pathname and adds any auth tokens, etc... This makes it work without making any changes on local, QA, and Production.

I just wish there was some kind of documentation as to why it was changed. Looked through all the changelogs too just to see if there is a mention of it anywhere, but couldn't find any.

Forgot to mention. @PapaNappa there is a site for migration https://update.angular.io/ just in case you didn't know about it just like i didn't.

For a base HREF (as per the specification), the meaning of /virtualDir and /virtualDir/ are different. The first signifies the last segment is a file and the second signifies that the last segment is a directory. Both are valid base HREFs.

For the use case outlined above, ng build --base-href /virtualDir/ --prod should be sufficient. The deploy URL option is essentially unnecessary if using the base HREF option. For a comparison, please see https://github.com/angular/angular-cli/blob/master/docs/design/deployurl-basehref.md

thanks for the reply. Looks like using base-href and deploy-url with / on both sides was what caused the issue.

Closing this as resolved. If you are still running into issues, then please feel free to re-open.

I found that the deploy-urlworks correctly with ng build, but is completely ignored with ng serve. Can anyone confirm it?

It works for me if you configure it within the angular.json as I have shown above.

On 11 May 2018 14:48:13 CEST, attmik notifications@github.com wrote:

I found that the deploy-urlworks correctly with ng build, but is
completely ignored with ng serve. Can anyone confirm it?

Have the same issue.
Before with 'ng serve --deploy-url /ui/' I had http://localhost:4200/ui/main.js
Now nether --deploy-url /ui/ nor configuration projects-serve-options: "deployUrl": "/ui/" don't work
I have http://localhost:4200/main.js

Anyone resolve this problem?
or is there some other ways to replace "--deploy-url"?

I've used approach with proxy.conf.json set pathRewrite to correct path
something like
"/notcorrect/blabla/*": {
"target": "http://localhost:4200",
"logLevel": "debug",
"secure": false,
"pathRewrite": {
"^/notcorrect/blabla/": "/blabla/"
}
}

@attmik I have the same problem; ng build --deploy-url /assets/ works fine for me where my static assets are being requested from the /assets/ path correctly in prod. But I think I got it working now. I was looking at the angular.json schema definition page. There are links to each section used by the angular.json file. Looking at the schema for the build target, it appears that the build target does have an option for deployUrl. I configured _that_ in my angular.json file and now the static assets have the right path when I load my app locally through the dev-server.

I think what is going on is that the ng serve may not be passing the --deploy-url cli option to the build target. It is interesting because, the dev-server target itself has its own --deploy-url option, possibly because it can then pass that value to the dependent build target? I am not sure, though.

@qmy777 PapaNappa's solution works!

add "baseHref": "/dir", "deployUrl": "/dir" to projects.Angular6.architect.build.options

projects.{projectname}.architect.build.options.deployUrl on angular.json worked for me.

projects.{projectname}.architect.build.options.deployUrl

It will set deployUrl to all configuration environment like I have Production and dev(for testing). I want deployUrl only for production. then how can I do that in angular.json file.

@abhishek11210646 just add "baseHref": "/dir/" and "deployUrl": "/dir/" under projects.{projectname}.architect.build.configurations.{env}

I think this needs more attention. Took us days to get the configuration just right, so I share the frustration of the OP. I will share our working experience:

Adding to @zcsongor, if you want HMR to work, you also need to add "baseHref": "/dir/" and "deployUrl": "/dir/" to {projectname}.architect.serve.configurations.{env} or {projectname}.architect.serve.options because ng serve will need to be told too.

This is very relevant when you want to serve Angular from within Asp.NET core, where the app should be distributed in "wwwroot/dist"

in angular.json, our {projectname}.architect.build.options looks like this:

"outputPath": "wwwroot/dist",
"baseHref": "/dist/",
"deployUrl": "/dist/",
"index": "ClientApp/index.html",
"main": "ClientApp/main.ts",
"polyfills": "ClientApp/polyfills.ts",
"tsConfig": "ClientApp/tsconfig.app.json",
"assets": [
    "ClientApp/resources"
],
"styles": [
    "ClientApp/styles.scss"
],
"scripts": [
    "node_modules/jquery/dist/jquery.min.js",
    "node_modules/popper.js/dist/popper.min.js",
    "node_modules/bootstrap/dist/js/bootstrap.min.js"
]

and in angular.json, our {projectname}.architect.serve.options looks like this:

"browserTarget": "PROJECT NAME:build",
"baseHref":  "/dist/",
"deployUrl":  "/dist/",
"hmrWarning": false

and if you use ASP.NET core and want HMR and LazyLoad to work, you will want to add hmr config as shown here https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/configure-hmr.md in your angular.json and use a conditional block MapWhen() like shown below to send all the JS and websocket calls to the ng serve process in the background. (pardon the poor regex)

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ... other service config above...

    // configuration so UseSpaStaticFiles works (see below)
    services.AddSpaStaticFiles(configuration =>
    {
        configuration.RootPath = "wwwroot/dist";
    })
};

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    ... other stuff above ...

    if (env.IsDevelopment())
    {
        // send all the Javascript queries to the node process, along with the websocket calls from devpack
        app.MapWhen(x => Regex.IsMatch(x.Request.Path.ToUriComponent(), "^/(.*.js|sockjs-node/.*)"), (app2) =>
        {
            app2.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";
                // add script "start-hmr": "ng serve --configuration hmr" to package.json
                spa.UseAngularCliServer(npmScript: "start-hmr"); 
                // alternatively, with a separate ng serve, but we never got this to work
                //spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
            });
            // end of the pipeline for javascript files or web sockets.
        });

        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseSpaStaticFiles();

        // must be last.
        app.UseMvc(routes =>
        {
            // MVC hook up so we can process /api calls and the home controller initial view.
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            // required so the 404s are sent to the browser app. ex: /system/users may be a valid route for the browser, but not MVC
            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });
    }
    else
    {
        // must be first
        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseSpaStaticFiles();

        // production => no spa services.
        // setup the MVC routes that we will be serving.
        app.UseMvc(routes =>
        {
            // MVC hook up so we can process /api calls and the home controller initial view.
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            // required so the 404s are sent to the browser app. ex: /system/users may be a valid route for the browser, but not MVC
            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });
    }
}

@qmy777 PapaNappa's solution works!

add "baseHref": "/dir", "deployUrl": "/dir" to projects.Angular6.architect.build.options

Don't forget to add "/" end of dir. Like "baseHref": "/dir/"

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

IngvarKofoed picture IngvarKofoed  路  3Comments

brtnshrdr picture brtnshrdr  路  3Comments

donaldallen picture donaldallen  路  3Comments

delasteve picture delasteve  路  3Comments

rajjejosefsson picture rajjejosefsson  路  3Comments