quasarframework / quasar

Quasar Framework - Build high-performance VueJS user interfaces in record time
https://quasar.dev
MIT License
25.97k stars 3.53k forks source link

Relative publicPath with history router mode adds prefix '/' to base href #8513

Open span opened 3 years ago

span commented 3 years ago

Describe the bug When setting publicPath to './' and using history router mode, the base href inserted into the html contains an extra prefix /.

This means that I cannot deploy my application to a dynamic environment where it will be hosted at both www.example.com and www.someotherexample.com/apath.

To Reproduce Steps to reproduce the behavior:

  1. Configure vueRouterMode to 'history' in quasar.conf.js
  2. Configure publicPath to './', or '.' or '' in quasar.conf.js
  3. Run project

Expected behavior I expect the <base href to contain a relative path.

Screenshots

image

Platform (please complete the following information): Quasar Version: @quasar/app Version: Quasar mode: [X] SPA [ ] SSR [ ] PWA [ ] Electron [ ] Cordova [ ] Capacitor [ ] BEX Tested on: [X] SPA [ ] SSR [ ] PWA [ ] Electron [ ] Cordova [ ] Capacitor [ ] BEX OS: Mac Node: 10.23 NPM: 6.14.11 Yarn: Not installed Browsers: Firefox, Chrome iOS: Android: Electron:

❯ quasar info

Operating System - Darwin(19.6.0) - darwin/x64
NodeJs - 10.23.0

Global packages
  NPM - 6.14.11
  yarn - Not installed
  @quasar/cli - 1.1.3
  @quasar/icongenie - Not installed
  cordova - Not installed

Important local packages
  quasar - 1.15.4 -- Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
  @quasar/app - 2.1.15 -- Quasar Framework local CLI
  @quasar/extras - 1.9.17 -- Quasar Framework fonts, icons and animations
  eslint-plugin-quasar - Not installed
  vue - 2.6.12 -- Reactive, component-oriented view layer for modern web interfaces.
  vue-router - 3.2.0 -- Official router for Vue.js 2
  vuex - 3.6.0 -- state management for Vue.js
  electron - Not installed
  electron-packager - Not installed
  electron-builder - Not installed
  @babel/core - 7.12.17 -- Babel compiler core.
  webpack - 4.44.2 -- Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.
  webpack-dev-server - 3.11.0 -- Serves a webpack app. Updates the browser on changes.
  workbox-webpack-plugin - Not installed
  register-service-worker - 1.7.1 -- Script for registering service worker, with hooks
  typescript - 3.9.5 -- TypeScript is a language for application scale JavaScript development
  @capacitor/core - Not installed
  @capacitor/cli - Not installed
  @capacitor/android - Not installed
  @capacitor/ios - Not installed

Additional context Issue #4451 describes the same issue but with full urls. The fix in commit f70bf797301002fc9c0f0403a44c574dc2559062 only seems to respect http paths. It is obvious from looking at the code that any path not starting with a / will get a leading /. Even passing in null or 'empty string', ends up with a base href of /.

Perhaps this is by design and this scenario is not supported?

eyedean commented 3 years ago

I had a similar problem and having appBase="" helped me not have the <base> tag. In my case I found having <base> causes more harm than good; YMMV, but you might want to reconsider.

My findings are at: https://github.com/quasarframework/quasar/issues/4687#issuecomment-770438618

mvalero commented 2 years ago

We are experiencing the same problem on our Quasar based VueJS application.

Our vue app is deployed on the cloud where several instances for different customers sit behind an ambassador (envoy proxy) which adds a prefix to each customer's instance. i.e https://a.b.com/customer1/anyRouteInMyVueApp.

For that reason we need all static resources linked in our index.html to have relative paths like ./file1.css and not /file1.css otherwise they would be incorrectly routed to https://a.b.com/ instead of to https://a.b.com/customer1`.

The problem is when we use:

      vueRouterMode: 'history', // available values: 'hash', 'history'
      publicPath: '.',

the generated paths in the index.html are always preceded and followed by slashes like: /./css/vendor.d836bb7c.css

Interestingly when using:

      vueRouterMode: 'hash', // available values: 'hash', 'history'
      //publicPath: '.',

(note publicPath has been explicitly not set in this case) the relative path is respected in the final index.html.

I can reproduce following scenarios using quasar build:

The modification of the public path seems to happen here: https://github.com/quasarframework/quasar/blob/dev/app/lib/quasar-conf-file.js#L40

I created a simple standard vuejs project without quasar just to test if that works there and in that scenario publicPath: '.' leads to correct relative paths.

Is there a reason why relative paths are not allowed/avoided? Or is the publicPath formatting actually behaving incorrectly?

We'd like to use history routing in our project but with that limitation we can only use hash routing at the moment.

eyedean commented 2 years ago

@mvalero

  1. Why do you need to have individual CSS files for each customer? (i.e. ./file1.css for https://a.b.com/customer1/anyRouteInMyVueApp)

  2. I just looked at my code and publicPath is set to /foo in dev environment for me, and https://cdn.domain.com/artifacts/v1/ in production. I don't use empty or .. However appBase: "" is something I have.

  3. I believe https://github.com/bripkens/connect-history-api-fallback is what you need to catch all the landing paths. You can then serve the app from https://a.b.com (and CSS file being under a.b.com/css/file1.css). Then each customer path would be a route/nested-route. Take a look at that package and nested-routes of Quasar ❤️: https://quasar.dev/layout/routing-with-layouts-and-pages#nested-routes

mvalero commented 2 years ago

@eyedean thanks for the response.

It is not that we need individual CSS files for each customer. In our case each customer has its completely separate instance of the app for both front-end and back-end. All of these instances are running inside a Kubernetes cluster and we use an envoy proxy (ambassador) in charge to route incoming requests (i.e a.b.com/customer1/) to the respective kubernetes service which is running that particular instance.

The problem with that scenario is, for the index.html of the VueJS application running behind a.b.com/customer1/, the root path will still be a.b.com and not a.b.com/customer1/ and therefore the browser will try to fetch all resources which are linked with an absolute path (i.e /file1.css) from a.b.com. But there is no application server running there at all.

We are addressing that problem in another applications using relative paths for our static resources, but quasar does not seem to allows us doing that.

We are not using a cdn in our project so the static resources are currently being served from the main application server.

I hope the issue is clearer now.

eyedean commented 2 years ago

@mvalero based on what I'm hearing, it's a bigger architectural discussion than a quasar bug. I'm sorry that I cannot be more helpful. I hope other developers can respond to you here, but at the same time maybe if you post it in Stackoverflow, folks over there can also provide suggestions. Good luck!

PS. If you posted it on SO, please send a link here. I'd be happy to take a look at it there. :)

mvalero commented 2 years ago

@eyedean I don't completely agree on that, the architectural context was provided to depict the use case where we are facing that issue. But regardless from the architectural context there is a mismatch in the behavior from vuejs cli and quasar cli where quasar seems to be explicitly avoiding relative public paths.

See https://cli.vuejs.org/config/#publicpath where its stated publicPath can also be relative. After testing both vanilla vue cli with publicPath: '.' the generated paths on vue cli are href="css/app.xxx.css" and on quasar cli href="/./css/app.xxx.css"

So my main question regarding quasar's behavior would be, is there a reason why a relative publicPath is being explicitly avoided and therefore behaving differently that vanilla VueJS cli?

CurryAyam commented 2 years ago

any solution for this problem? i'm getting the same issue with the vite version!

philnagel commented 2 years ago

Adding the following to build in my quasar.config.js file resolved this issue for me:

    build: {
      extendViteConf(viteConf, { isServer, isClient }) {
        viteConf.base = "";
      },
      ...
    }

This is if you are using vite.

josegduarte96 commented 2 years ago

Adding the following to build in my quasar.config.js file resolved this issue for me:

    build: {
      extendViteConf(viteConf, { isServer, isClient }) {
        viteConf.base = "";
      },
      ...
    }

This is if you are using vite.

Thanks, is working!!!

mgd722 commented 1 year ago

setting this directly in vite is what solved it for me, but it sure seems like a hacky workaround for inconsistent behavior. is there a reason it was done this way, or just oversight?

Archer6621 commented 1 year ago

setting this directly in vite is what solved it for me, but it sure seems like a hacky workaround for inconsistent behavior. is there a reason it was done this way, or just oversight?

Agreed, this seems like basic functionality that should just work. I had found this thread earlier: https://forum.quasar-framework.org/topic/8007/build-spa-app-with-relative-base-path/8

But for some reason this no longer works, at least on version: @quasar/app-vite 1.4.0 (@quasar/cli 2.0.0)

The Vite override works fine though.

carlosk75 commented 1 year ago

Hi all, Having the same problem and the workaround by setting "viteConf.base" does not seem to work. Any idea?

Thanks

morebrackets commented 11 months ago

This works for me:

build: { vueRouterMode: 'history', vueRouterBase: '/', publicPath: '/afolder', distDir: '/var/www/example.com/afolder', }

This loads properly with example.com/afolder or any alias like example.com/afolder2

You can add the alias paths in Nginx like:

location ~ ^/(afolder2|afolder3)$ { alias /var/www/example.com/afolder; try_files $uri $uri/ /index.html?$args; }

matthewadams commented 7 months ago

We have exactly the same issue. Somehow I missed this when I added my question about the same thing.

matthewadams commented 7 months ago

@eyedean My request would be to add (and clearly document) a feature that allows vueRouterBase to have a value of . or ./, which would allow fully relative path-based deployments of the SPA (and whatever other types for which this is appropriate).