48
votes

Maybe 8-9 months ago I created a Webpacked Vue.js project with vue-cli and was able to modify /build/webpack.dev.conf.js to have it put the "compiled" index.html and JavaScript / CSS files in the right folders in my Flask app when I run npm run build.

I am now showing someone else how to create a Vue.js / Flask app and I see that the way vue-cli works seems to have changed, so that I no longer have access to the /build/ folder.

I read the docs and they seemed to say that it now abstracts away the Webpack config ("Since @vue/cli-service abstracts away the webpack config..."), but that if I want to see Webpack's config options, I can do vue inspect > output.js. I did that and don't see the entries in there that I changed when I did this eight months ago:

/build/webpack.prod.conf.js:

new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'testing'
-    ? 'index.html'
+    ? 'app.html'
    : config.build.index,
-  template: 'index.html',
+  template: 'app.html',

/build/webpack.dev.conf.js:

new HtmlWebpackPlugin({
-   filename: 'index.html',
-   template: 'index.html',
+   filename: 'app.html',
+   template: 'app.html',

/config/index.js:

module.exports = {
  build: {
    env: require('./prod.env'),
-    index: path.resolve(__dirname, '../dist/index.html'),
-    assetsRoot: path.resolve(__dirname, '../dist'),
-    assetsSubDirectory: 'static',
-    assetsPublicPath: '/',
+    index: path.resolve(__dirname, '../../server/templates/app.html'),
+    assetsRoot: path.resolve(__dirname, '../../server/static/app'),
+    assetsSubDirectory: '',
+    assetsPublicPath: '/static/app',

It looks like the vue build command-line command can accept an argument that allows you to specify the output directory, but I need to specify two different directories: one for the HTML file (which should live in Flask's /templates/ folder), and another for the JavaScript / CSS code (which should go in Flask's /static/ folder).

6
What exactly are you looking for in output.js file? I just ran this command and there is entry and output properties that refer to the directories you are trying to change. Have you tried to override them in vue.config.js as the documentation says?oniondomes
@oniondomes I've updated my question to include the files and lines I changed last time. As I mention in the question (added maybe 10-20 minutes ago so maybe you didn't see it), I want to output the HTML file to one directory and the JavaScript / CSS to another directory.Nathan Wailes
does anyone now if it's possible to pass args in the npm run command? for example: npm run build --output-path /my-path ?DAG
@DAG Yes, see this SO answer: stackoverflow.com/a/14404223/4115031Nathan Wailes

6 Answers

53
votes

I just had to do this for a new project using the latest Vue-CLI and it was pretty simple: I just needed to have a file called vue.config.js at the top-level of my Vue project and within it I needed the following code:

const path = require("path");

module.exports = {
  outputDir: path.resolve(__dirname, "../backend/templates/SPA"),
  assetsDir: "../../static/SPA"
}

Warning: Vue-CLI will delete the contents of whatever folders you specify to use for its output. To get around this, I created the "SPA" folders within my templates/ and static/ directories.

Note also that the assetsDir is specified relative to the outputDir.

13
votes

Per the related vuejs guide

WARNING

Some webpack options are set based on values in vue.config.js and should not be mutated directly. For example, instead of modifying output.path, you should use the outputDir option in vue.config.js; instead of modifying output.publicPath, you should use the baseUrl option in vue.config.js. This is because the values in vue.config.js will be used in multiple places inside the config to ensure everything works properly together.

And the related vuejs config reference

TIP

Always use outputDir instead of modifying webpack output.path.

Here's an example vue.config.js that I just verified using vue-cli-service @ 3.0.0-rc.2

const path = require("path");

module.exports = {
  outputDir: path.resolve(__dirname, "./wwwroot/dist"),
  chainWebpack: config => {
    config.resolve.alias
      .set("@api", path.resolve(__dirname, "./src/api"));
  },

  pluginOptions: {
    quasar: {
      theme: 'mat'
    }
  }
}
9
votes

By default, production files are built into the /dist/ subfolder of your Vue project and they are supposed to be deployed in the root folder of your server ("/"). Hence you will have to copy them to root in order to access the project from your browser. Since this is not always convenient, you may wish to build them for deployment in a subfolder of root e.g. /subdir/vue_project/dist/.

In this case, follow the advice at https://cli.vuejs.org/config/#publicpath to redirect vue-cli to build the project files for this subfolder. To do this, please execute vue ui, then open the cli 3.3+ UI Configuration window at localhost:8000 then change the default value of publicPath to /subdir/vue_project/dist/ and Save changes.

If you wish to go back to development (serve), do not forget to set publicPath back to / before executing serve and accessing localhost:8080.

6
votes

I struggled with this yesterday myself, but managed to crack it in the end by using part of oniondomes answer and some of the new documentation:

const path = require('path');    
module.exports = {
    configureWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            config.output.path = path.resolve(__dirname, './server/assets/static');
            config.output.publicPath = '../assets/static/';
            config.output.filename = '[name].js';
        }
    },
    chainWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            config
                .plugin('html')
                .tap(args => {
                    return [
                        {
                            filename: path.resolve(__dirname, './server/templates/test.html'),
                            template: path.resolve(__dirname, './public/index.html')
                        }
                    ];
                });
        }
    }
}

The key difference is the chainwebpack section - this allowed you to override any existing configs for plugins referenced. The other answer is adding another html-webpack-plugin which I believe is causing it to throw an error.

I originally had the configs as objects, but this caused issues when trying to run in dev mode. By changing them to functions you can specify that this only occurs when building in production mode.

At any point you can see the webpack config generated with these overrides by running the following from the console

vue inspect > output.js

The issue I needed to fix was specific to an C# MVC project - needing to output to a cshtml page. I'll leave my solution to that here for anybody that needs it.

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:60263',
                changeOrigin: true
            }
        }
    },
    configureWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            config.output.publicPath = '/dist/';
            config.entry = ['babel-polyfill', './src/main.js']
        }
    },
    chainWebpack: config => {
        if (process.env.NODE_ENV === 'production') {
            config
                .plugin('html')
                .tap(args => {
                return [
                    { filename: '../Views/Home/Index.cshtml', template: 'public/index.html' },
                ]});
        }
    }
}
3
votes

To be honest I don't see what problem you're having since you have seen the docs and pointed directly to the right section of it. I just created a new vue.js project using vue-cli 3.0, created vue.config.js file in project's root directory and looking at output.js (created automatically with vue inspect > output.js) I have written the following:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  configureWebpack: {
    output: {
      path: path.resolve(__dirname, './server/assets/static'),
      filename: '[name].js',
      publicPath: '../assets/static/',
    },
    plugins: [
      new HtmlWebpackPlugin({
        filename: path.resolve(__dirname, './server/templates/test.html'),
        template: path.resolve(__dirname, './public/test.html'),
      }),
    ],
  },
};

Now when I run build script my files goes to the another-dir folder and html code is taken from test.html. I guess following this method you can reconfigure your project according to your need.

Hope I did understand you question right and didn't waste everyone's time.

EDIT: this seems to be placing files as you want, but for some reason HtmlWebpackPlugin happens to create dist/ folder and output .html file twice in both /dist and /templates. To this one I couldn't find a solution yet.

3
votes

With newer versions of Vue.js, you just have to set the proper publicPath in your the file vue.config.js on the root folder:

// vue.config.js file to be place in the root of your repository

module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/my-project/'
    : '/'
}

More infos: https://cli.vuejs.org/guide/deployment.html#general-guidelines