4

I have a Vue app served in IIS. I need the dist folder to have a custom web.config file as per instruction here. The problem I have now is whenever I run "npm run build", the whole dist folder gets nuked, and my web.config file is lost, and I need to create one again. Since the file is rather small, this isn't really a big of a deal but I really would like to automate this.

The way I work now is that I go to my test machine, git pull the main dev branch, execute "npm run build", create the web.config file and put it to the dist folder. I'm guessing I can do this in my CI/CD pipeline (I don't have one... yet), but I would like to know if there are any way to do this outside of CI/CD? Any way I can run "npm run build" and it'll automatically generate/copy a web.config file for me?

user1491884
  • 589
  • 1
  • 5
  • 12

2 Answers2

8

Vue CLI generated projects are configured to copy anything in the public folder directly into the build output directory (dist by default). They achieve this with the Webpack Copy plugin.

The only exception is the index.html file which undergoes some pre-processing to inject scripts and stylesheets before being copied.

With that in mind, put your web.config file in the public directory and it will be included verbatim in your builds.

Phil
  • 157,677
  • 23
  • 242
  • 245
  • Doing this will result in the pwa cache including the web.config as a file that the browser should request – Blowsie Jul 23 '21 at 09:16
  • @Blowsie, so I have the issue same as your comment, could you already fix it? – Park Aug 01 '22 at 10:28
  • 1
    @Park try the answer in this post. [workbox webpack plugin exclude folder from precache manifest](https://stackoverflow.com/q/55438555/283366) – Phil Aug 02 '22 at 00:27
2

@Phil's answer is simple and perfect if you have an invariant web.config file that does not need any variable expansion. However, if you'd like to generate a web.config file that includes, for example, an Rewrite rule with the BASE_URL, the following will work:

In vue.config.js:


"use strict";

// Do NOT use the HtmlWebpackPlugin name to avoid a collision with standard Vue usage of the html-webpack-plugin
// NOTE also this uses the vue-cli-service dependency path for html-webpack-plugin
// Beware if the require() path is incorrect, it may fail silently, thus ignoring vue.config.js (per (per https://github.com/vuejs/vue-cli/issues/5442)
const HtmlWebpackPlugin2 = require('@vue/cli-service/node_modules/html-webpack-plugin');

module.exports = {
  devServer: {
    disableHostCheck: true,
  },
  transpileDependencies: ['vuetify'],
  publicPath: process.env.NODE_ENV === 'production' ? process.env.BASE_URL : '/',
  configureWebpack: {
    plugins: [
      new HtmlWebpackPlugin2({  //  generate web.config
        title: 'web_config',
        template: 'src/web.config',
        filename: 'web.config',
        inject: false
      })
    ]
  }
}

A key element is to declare const HtmlWebpackPlugin2 = require('@vue/cli-service/node_modules/html-webpack-plugin'); using a name other than HtmlWebpackPlugin to avoid clashing with the vue-cli-service's use of HtmlWebpackPlugin. Then, in the configureWebpack: element, add the "new" HtmlWebpackPlugin2 plugin with the template path to your web.config template. The inject: false option is also important to avoid injecting <head> and <script> tags into your web.config XML.

As noted in the OP's question, to run under IIS, the web.config file should include a rewrite rule that rewrites client-side routing paths to your virtual root (also described here). This can be accomplished with html-webpack-plugin's variable expansion syntax <%= process.env.MY_VARIABLE_NAME %>:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
            <rule name="SPA Routes" stopProcessing="true">
                <!-- match everything by default -->
                <match url=".*" />
                <conditions logicalGrouping="MatchAll">
                    <!-- unless its a file -->
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <!-- or a directory -->
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    <!-- or is under the /api directory -->
                    <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                    <!-- list other routes or route prefixes here if you need to handle them server side -->
                </conditions>
                <!-- rewrite to application root -->
                <action type="Rewrite" url="<%= process.env.BASE_URL %>" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

The BASE_URL variable itself is defined in a .env.production file in same folder as vue.config.js as described here or here. It is a plain text file that contains simply:

BASE_URL="/my-vroot-name/"

With the above, your standard yarn build command will perform variable substitution in your src\web.config template and write the output to dist\web.config.

When setting up your IIS virtual application, make sure your physical path points to the dist folder, or adjust the BASE_URL and HtmlWebpackPlugin2 output filename paths as needed for your server environment.

Martin_W
  • 1,582
  • 1
  • 19
  • 24