angular / angular-cli

CLI tool for Angular
https://cli.angular.io
MIT License
26.76k stars 11.98k forks source link

consider "output" path in serve mode for assets configuration #7773

Closed elvisbegovic closed 7 years ago

elvisbegovic commented 7 years ago

Repro steps.

I have multi language app, I know I need build for each language and actually output is:

/fr -> /assets
/de -> /assets

with this configuration in angular-cli

"assets": [
        "assets",
        "favicon.ico"
      ],

And in all my template component I use ressources from assets like : <img src="assets/images/avatar2.jpg" />

This ABOVE works for ng serve and ng b !

PROBLEM:

What I want is: have only one /assets folder like

/fr
/de
/assets

So I simply edit my angular-cli config file to put assets one level higher :

"assets": [
        { "glob": "**/*", "input": "./assets/", "output": "../assets/" },
        { "glob": "favicon.ico", "input": "./", "output": "../" }
      ],

And in all my template I juste specify one level higher like : <img src="../assets/images/avatar2.jpg" />

PROBLEM: this only works on ng build NOT on ng serve !

The log given by the failure.

image

Mention any other details that might be useful.

to build I simply use "ng build --prod --output-path dist/fr --locale fr --bh /fr/ --i18n-format xtb --i18n-file src/locale/fr.xtb

What I expect

Is it possible to considere "output" path of assets in DEVELOPEMENT mode server ? Or how achieve this behavior ?

filipesilva commented 7 years ago

What is happening here is that <img src="../assets/images/avatar2.jpg" /> tries to go up one directory, but on the server there is no directory above. The server gives you http://localhost:4200, but you are asking for http://localhost:4200/... which doesn't exist. That is why your app fails to load the resources at runtime.

The --output-path options is not related to locale, or assets, or language so it doesn't make sense to add that sort of functionality IMHO.

Since what you really need is to load different things (images etc) at runtime depending on the app locale, my advice is to use the LOCALE_ID token (https://angular.io/api/core/LOCALE_ID). Then you can use interpolation in <img src="../assets/{{LOCALE_ID}}/images/avatar2.jpg" /> to load your assets from the right folder.

elvisbegovic commented 7 years ago

Please apologize, but not sure we are not talking about the same thing.

I'm not talk about --output-path but this


 "assets": [
        {
          "glob": "**/*",
          "input": "./assets/",
          "output": "./assets/" <---- THIS i mean have something  like output-dev and output-dist
        },{
          "glob": "favicon.ico",
          "input": "./",
          "output": "./"  <---- THIS i mean have something like output-dev and output-dist
        }
      ],

On DEVELOPEMENT mode ng serve I'm not sure that <img src="../assets/images/avatar2.jpg" /> tries to go up one directory or I don't know HOW but actually with this configuration ABOVE I write in template <img src="../assets/images/avatar2.jpg" /> and it works .

This works on DEV ng serve but if I want to get it working on ng build I ned edit in config this "output": "./assets/" to "output": "../assets/"

That's why I wonder how can we specify for dev to go to ./assets path (which have no directory above) and on production put my /assets outside /fr and /de distribution

Actually I solved this with 2 apps[] and only one character change from . to ..

My assets are about 5mo and don't want duplicate them in /fr and /de dist just because of i18n non-runtime compilation, it's about cache performance too.

@filipesilva

filipesilva commented 7 years ago

I'm not sure what you want me to apologize for :/

Thanks for the clarification that you're talking about the "output" property in the assets array and not the --output-path flag in your example. We're not looking at adding per-environment asset output, as environments are arbitrary and we only use them for the environment functionality proper.

In ng serve, when you use <img src="../assets/images/avatar2.jpg" /> on a template, your browser will try to resolve that path relative to the url. So if you are on http://localhost:4200/, the full request url will be http://localhost:4200/../assets/images/avatar2.jpg, which is resolved to http://localhost:4200/assets/images/avatar2.jpg - notice how the /.. went away because there is no folder up. You can test this on a new project with ng serve. So essentially the ../ is ignored.

In your production server maybe you are not serving at the root. If you serve at http://myserver.com/mysite/, and request <img src="../assets/images/avatar2.jpg" />, the full url will be http://myserver.com/mysite/../assets/images/avatar2.jpg. This is then resolved to http://myserver.com/assets/images/avatar2.jpg - notice how the /mysite/.. went away because the there was a folder above to go up to.

If you see something working when you do ng serve but not when you do ng build and then deploy it to your server, it likely means that something is different between your server and the ng serve development server. Pay close attention to your folder structure, base url, public path, and anything else that can affect the browser requests. You can also find more information about deployment in https://angular.io/guide/deployment.

elvisbegovic commented 7 years ago

@filipesilva Wanted to say "apologize me" to be confuse because my english's limited and thanks a lot for theses infos but we are still diagree: I'd say thanks for your link about deployment but it's not question about "understand deployment and base url and so" this I understand.

Here's really question about new feature and how this output is used if we are on ng s or ng b, and if you don't see requirement let me try with another way:

As you explain you agree this: on ng serve if I write in template "../assets/images/avatar2.jpg" the browser will try to resolve that path relative to the url. So if I'm on http://localhost:4200/, the full request url will be http://localhost:4200/../assets/images/avatar2.jpg, which is resolved to http://localhost:4200/assets/images/avatar2.jpg So essentially the ../ is ignored EXACTLY WHAT YOU SAID AND IT'S COOL.

My question always on ng serve is to apply EXACTLY same behavior "essentially ../ ignored" in angular-cli.json So if I write:

"assets": [
        {
          "glob": "**/*",
          "input": "./assets/",
          "output": "../assets/"
        },{
          "glob": "favicon.ico",
          "input": "./",
          "output": "../"
        }
      ],

On ng serve the ../ wnt away because there is no folder up, AND for ng build respect it and put it onle folder UP.

This behavior will really be nice, only one assets on prod (which is exactly same for each language) My /assets is about 5-10mo, theses images will be on cache if we switch language and so on and so on... Hope this time I'm clear.

filipesilva commented 7 years ago

Ok I understand the please apologize expression now! Don't worry about the language difference, english isn't the first language for most people (it isn't for me either).

Oh I think I understand better... so the folder structure that you want when you do ng build --output-path=dist/fr/ is this:

dist/
  assets/ (made from assets array)
  fr/ (made from assets array AND build command)

So your dist/fr contains all contents from your build AND from src/assets/fr. That is a more difficult question... And https://github.com/angular/angular-cli/pull/7778 will actually make that setup not work anymore.

It is true that the current setup allowed for assets to be written outside of the output path but that was really not intentional as it is the cause of a lot of silent errors. In your case you were using it consciously to obtain that behaviour due to how your deployment works.

I think that the best alternative for you is to use multiple apps: https://github.com/angular/angular-cli/wiki/stories-multiple-apps

With multiple apps you can set different outDir and assets, while leaving all the other properties the same. You'd still have to manually copy favicon though... but this the correct answer because you really are building two different apps, it just so happens that you have your production server serving them from the same parent folder.

I understand that you can feel this could be easier, and it's true that i18n builds are somewhat messing right now (you always have to build multiple times, and deploy multiple times, etc, see more in https://github.com/angular/angular-cli/issues/6789). But improvements are planned for the future (https://github.com/angular/angular-cli/issues/6789#issuecomment-316036805).

elvisbegovic commented 7 years ago

@filipesilva yes I think we are nearly my question just little mistake because I don't have src/assets/fr as you write

Otherwise you are correct I can achieve my behavior with 2 apps in angular-cli (as I said in my second post).

Here is the actual solution of my requirement. To build different language I run npm run ok here details:

  "scripts": { 
...
    "fr": "ng build --prod --app=prod --output-path dist/fr --locale fr --bh /fr/ --i18n-format xtb --i18n-file src/locale/fr.xtb",
    "de": "ng build --prod --app=prod --output-path dist/de --locale de --bh /de/ --i18n-format xtb --i18n-file src/locale/de.xtb",
    "ok": "npm run fr && npm run de && cd dist && 7z a dist -mx9",
    "report": "ng build --prod --output-path dist/fr --locale fr --bh ./ --i18n-format xtb --i18n-file src/locale/fr.xtb --stats-json && webpack-bundle-analyzer dist/fr/stats.json"
  },

And in my angular-cli.json I have two apps named dev and prod

You can remark that both app are completly same the only thing different is ONE CHARACTER in assets output in app called dev I have "./assets" and in app called prod I have "../assets"

With this configuration I can run my npm run ok and it gives me: image

I have only one /assets folder, in others words there is not /assets folder in /fr or /de build.

https://github.com/angular/angular-cli/pull/7778 that beats me :) but not sure my question couldn't work with.

Advantage with my actual cli configuration is I can keep for others developers on my team to use simply ng serve to run fast dev-mode without sepcifying --app=dev (I think it takes first apps[] when I simply run ng s, nice) but for prod I need specify --app=prod and it still not annoying for me, because ng build is not done every day, what embarassing me is that I need duplicate apps[] for dev and for prod while only 1 more char is added in assets output ./ for dev and ../ for prod, I expect To have same approach as you treat template url if we write ../../../ for a image on ng serve it will simply went away and stay relativly on same folder (keeping output path).

I dont' realize how go ahead, to resume: treat differenty assets output depending if we are on ng serve or ng build. All assets.output[] you write as "../../../" should be resolved to ./ on ng serve and keeped "../../../" for ng build.

Now I think you see better, if not possible or not interessant sorry to make so much noise

filipesilva commented 7 years ago

I understand much better now, thank you. We can't make that change because on some setups it would break. You need it for your particular deployment setup but in other setups they need the ../../ etc. So changing it for them would break.

I appreciate you taking the time to explain this so well, I really do. It is very hard to understand the setups that people use sometimes and talking about it is the only way really.

elvisbegovic commented 7 years ago

Sorry to make noise one more time here, but after making workaround and have 2 app in my config file to get what I need : angular-cli.json

today I udpate to last version of cli 1.4.7 tried ng build this get error :

An asset cannot be written to a location outside of the output path. source file common.ts

This is very annoying for me. Because all was working with my workaround. Now I can't have ma requirement. Maybe this need to be Warning instead Error

@filipesilva

Viktor-Bredihin commented 6 years ago

why this issue is closed? Does anyone supposed that duplicated assets for each language is the correct way to build multilang app?

gamunax commented 6 years ago

@istiti
did you get to fix it?

gamunax commented 6 years ago

my project: /en /es /assets

my solution: 1) use Multiple Apps integration 2) my .angular-cli.json:

"name": "dev",
      "root": "src",
      "outDir": "dist",
      "assets": [        
        "favicon.ico",
        {
          "glob": "**/*",
          "input": "./assets/",
          "output": "./assets/"          
        }

"name": "prod",
      "root": "src",
      "outDir": "dist",
      "assets": [        
        "favicon.ico",
        {
          "glob": "**/*",
          "input": "./assets/",
          "output": "../assets/",
          "allowOutsideOutDir": true

        }

3) Build:

(production):

"build-i18n:es": "ng build --app=prod --output-path=dist/es --aot -prod --bh /es/ --i18n-file=src/i18n/messages.es.xlf --i18n-format=xlf --locale=es",
    "build-i18n:en": "ng build --app=prod --output-path=dist/en --aot -prod --bh /en/ --i18n-file=src/i18n/messages.en.xlf --i18n-format=xlf --locale=en",
    "build-i18n": "npm run build-i18n:es && npm run build-i18n:en"

optional: if they had problems with their images in css in el file: /src/styles.css

error:

.buscador_itinerario{
    background-image: url('../assets/themes/1/images/bg_full.jpg') !important;
}

solution:

.buscador_itinerario{
    background-image: url('/../assets/themes/1/images/bg_full.jpg') !important;
}

add: '/../assets'

Viktor-Bredihin commented 6 years ago

It's a shame that issue is closed and there is no real solution for multilang apps. @gamunax thanks for your temporary solution

Bat-Orshikh commented 5 years ago

Is there any official solution from angular team?

angular-automatic-lock-bot[bot] commented 5 years ago

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.