Vue-cli: assetsDir not relative to outputDir

Created on 26 Jul 2018  ยท  10Comments  ยท  Source: vuejs/vue-cli

Version

3.0.0-rc.5

Reproduction link

https://github.com/androiddrew/flaskme

Steps to reproduce

Create a project with the following structure:

top_level/
โ”œโ”€โ”€ backend
โ”‚ย ย  โ”œโ”€โ”€ static
โ”‚ย ย  โ””โ”€โ”€ templates
โ””โ”€โ”€ vue_project
    โ”œโ”€โ”€ dist
    โ”œโ”€โ”€ public
    โ””โ”€โ”€ src
 ```
With vue.config.js set to :

const path = require("path");

module.exports = {
outputDir: path.resolve('../../backend/static'),
assetsDir: './assets',
baseUrl: '/',
runtimeCompiler: undefined,
productionSourceMap: undefined,
parallel: undefined,
css: undefined,
chainWebpack: config => {
config.plugin('html')
.tap(args => {
args[0].filename = path.resolve('../backend/templates/index.html');
return args;
});
},
}

Build your vue_project. The result is creates an `index.html` with the correct paths in the href tags, but the static assets are placed relative to the default folder NOT the outputDir defined in your config file.

Setting the vue.config file to:

const path = require("path");

module.exports = {
outputDir: path.resolve('../../backend/static'),
assetsDir: '../../backend/static/assets',
baseUrl: '/',
runtimeCompiler: undefined,
productionSourceMap: undefined,
parallel: undefined,
css: undefined,
chainWebpack: config => {
config.plugin('html')
.tap(args => {
args[0].filename = path.resolve('../backend/templates/index.html');
return args;
});
},
}

Will place the static assets in the defined directory however the `index.html` href values are injected with the assetsDir path.


```

What is expected?

The expected behavior should be to have the assetsDir be a relative path from your outputDir. If the outputDir is undefined then assetsDir is a relative path from the default assetsDir

What is actually happening?

The assetsDir is being used for two separate items. 1. It is being used as the outputDir for the assets. 2. It is used in the url-loaders that inject your src/href/etc. values.


This directory setup above is for a Vuejs SPA client and a Python backend. The built files should be served by the backend, which necessitates their placement in the backend static and template folders. It is part of a python application that will be distributed as a python package.

Most helpful comment

img paths are also wrong in the built files. They are not relative to anything so you can not move.

All 10 comments

Thanks for providing a reporduction, even if I had some trouble getting it to work since the outputPath provided didn't work as you describe.

However, I think I know where your problem lies.

  • Your index.html is saved to a different directory than your assets
  • vue-cli assumes that you put all output into the same directory
  • you are looking for an option that would prepend all assets paths in your app code with path string that would make up for these different locations

Is that about right?

First of all, assetDir, which is meant to define a physical directory within the outputDir, in which to output the assets. So it's not the solution to your problem and can't be - and there's no other config option in vue.config.js that solves this either.

What you would instead need to do is to adjust the (file)name setting of all loaders & plugins that generate assets - so mainly all rules with a url-loader and file-loader (images) and the mini-css-extract-plugin.

Before we get to that though, I still have a question: are you really serving the final index.html, which is served to the user's browser, and the final asserts in the same relation to one another? What I mean by that: you seem to want to adjust the asset path to adjust for the different folders were the assets and the index.html are created in, but that doesn't have to be the same "path difference" that the browser sees later.

Edit: On second thought I thnk my proposal below will actually not solve that. Will try another look at it later

Anyway: You can run the following to get an overview of the config:

npx vue-cli-service inspect --rules --mode production
npx vue-cli-service inspect --plugins --mode-production
npx vue-cli-service --mode production > config.js

the last command will give you a config.js file that contains a detailed description of the whole config.

Adjusting the config would look something like this:

chainWebpack: config => {
  // that would have to happen for a bunch of rules:
  ['images',  'media',  'fonts'].forEach(rule => {
    config.module.rules(rule).use('url-loader')
      .tap(options => {
        options.name = path.join('your/relative/asset/path', options.name)
        return options
      })
  })

  // svg is the only one using file-loader
  config.module.rules('svg').use('file-loader')
    .tap(options => {
      options.name = path.join('your/relative/asset/path', options.name)
      return options
    })

  // and the extract-css plugin has to be adjusted as well.
  config.plugin('extract-css').tap(([options]) => {
    options.filename = path.join('your/relative/asset/path', options.filename)
    return [options]
  })
}

I can't tell you what your relative path should be since I have no idea what the

So your solution encouraged me to start over from scratch.

I was using the vue ui to run my build and I believe it is the source of the strange behavior I am seeing. When I use the below vue.config.js and run it via npm run build from the command line everything lands where I expected with the correct paths necessary to serve it up through a Python Flask app.

However, firing up vue ui in the same path then running the build task results in my index file being written to the one defined in the plugin override, but the assets get written to the default dist folder. It is as if the vue ui build task is ignoring the outputDir.

const path = require("path");

module.exports = {
  outputDir: '../flaskme/static',
  assetsDir: undefined,
  baseUrl: process.env.NODE_ENV === 'production'
   ? '/static'
   : '/',
  runtimeCompiler: undefined,
  productionSourceMap: undefined,
  parallel: undefined,
  css: undefined,
  chainWebpack: config => {
  // Change index.html output directory
  if (process.env.NODE_ENV === 'production'){
    config.plugin('html')
      .tap(args => {
        args[0].filename = path.resolve('../flaskme/templates/index.html');
        return args;
      });
  }
},
}

Try outputDir: path.resolve(__dirname, '../../flaskme/static'),

@Akryum outputDir: path.resolve(__dirname, '../../flaskme/static') resolves to the correct path but it then creates the whole output dir as a subdirectory under the vue directory(which may be another bug). It does not resolve the issue with the vue ui build task as I found above. Using the vue ui with this line still disregards the outputDir and places assets in the default dist directory.

The command line and the ui build commands exhibit different behavior when the outputDir is set outside of the vue project. That is think is the issue I am reporting.

The index file problem is actually a problem in html-webpack-plugin: it's filename option does not support absolute paths. I'd suggest opening an issue there.

Absolute path for outputDir is fixed in e7602abf .
In addition, the index file location can now be configured individually via a new indexPath option.

Closing as with the above fixes assetsDir is working as intended.

I'm struggling with this to now it's time to deploy my migrated vue cli 2 to vue cli 3 SPA. I want to put the static files in ../../static but also have the assets served up from href="/static/js/app.a7as86ad8.js" etc. See https://forum.vuejs.org/t/baseurl-at-but-files-within-index-html-to-be-served-at-static/45777/4

On vue cli 2, I had:

  build: {
...

    // Paths
    assetsRoot: path.resolve(__dirname, '../../../'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

so my SPA lived at app.example.com but within index.html files were served at /static/js and /static/css etc.

img paths are also wrong in the built files. They are not relative to anything so you can not move.

I was advised to use baseurl and adjust in my router config.

Was this page helpful?
0 / 5 - 0 ratings