Tony O'Hagan
Tony O'Hagan

Reputation: 22692

How to use ASP.NET / Razor partials to inject Vue/Webpack bundle into webpack HTML template

I'm attempting to add VueJS into a legacy ASP.NET app. To this I wish to use Razor's @Html.Partial() method to inject the CSS and JS files generated by Vue's webpack into the Header and Body of an existing and a complex Razor layout template file used on every page in the legacy app.

To support multiple webpack entry points (mult-page) we're using the pages feature of Vue CLI v3 with the following vue.config.js file to goal to generate vendor, common and app webpack chunks.

var fs = require("fs");
var path = require("path");

// Can be Vue App or Shared bundles
var appBasePath = "./src/apps/";

var template = path.resolve(__dirname, "src", "index.razor.ejs");
console.log("template=", template);

var pages = {};
// We search for index.js files inside basePath folder and make those as entries
fs.readdirSync(appBasePath).forEach(function(name) {
  var appMainJS = `${appBasePath}${name}/main.ts`; // entry point
  var outputHtml = `${name}.html`; // partial Head & Body

  console.log(appMainJS);
  console.log(outputHtml);

  if (fs.existsSync(appMainJS)) {
    pages[name] = {
      // entry for the app
      entry: appMainJS,
      // source template  - might be relative to ./public ???
      template,
      // output HMTL
      filename: outputHtml,
      inject: false,
      minify: false,
      // when using title option,
      // template title tag needs to be <title><%= htmlWebpackPlugin.options.title %></title>
      // title: name,
      // chunks to include on this page, by default includes
      // extracted common chunks and vendor chunks.
      chunks: ["chunk-vendors", "chunk-common", "index"], 
    };
  }
});

module.exports = {
  pluginOptions: {
    quasar: {
      treeShake: true
    }
  },
  transpileDependencies: [/[\\\/]node_modules[\\\/]quasar[\\\/]/],
  pages
};

The template file ./src/index.razor.ejs is a webpack template for the HTML Webpack Plugin and is intended to emit a Razor partial HTML file that accepts a true mobile value to insert the Header tags OR false to insert the Body tags.

@model System.Boolean

@if(Model)
{
  <% for (var css in htmlWebpackPlugin.files.css) { %>
    <link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
  <% } %>
} else {
  <% if (htmlWebpackPlugin.options.appMountId) { %>
    <div id="<%= htmlWebpackPlugin.options.appMountId%>"></div>
    <% } %>

    <% if (htmlWebpackPlugin.options.appMountIds && htmlWebpackPlugin.options.appMountIds.length > 0) { %>
    <% for (var index in htmlWebpackPlugin.options.appMountIds) { %>
    <div id="<%= htmlWebpackPlugin.options.appMountIds[index]%>"></div>
    <% } %>
    <% } %>

    <% if (htmlWebpackPlugin.options.window) { %>
    <script>
      <% for (var varName in htmlWebpackPlugin.options.window) { %>
        window['<%=varName%>'] = <%= JSON.stringify(htmlWebpackPlugin.options.window[varName]) %>;
      <% } %>
    </script>
    <% } %>

    <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
    <script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
    <% } %>

    <% if (htmlWebpackPlugin.options.devServer) { %>
    <script src="<%= htmlWebpackPlugin.options.devServer%>/webpack-dev-server.js"></script>
    <% } %>
}

Due to the required structure of the emitted Razor partial HTML I can't use a traditional HTML file that webpack's HTML Webpack Plugin is designed to use. Sadly the template ABOVE will only emit vendor and common chunks. I've attempted using this alternative template but it emits nothing ...

@model System.Boolean

@if (Model)
{
  <%= htmlWebpackPlugin.headTags %>
} else {
  <%= htmlWebpackPlugin.bodyTags %>
}

BTW ... the official example uses htmlWebpackPlugin.tags.headTags and htmlWebpackPlugin.tags.bodyTags but that throws an exception that tags is undefined.

Upvotes: 1

Views: 1195

Answers (1)

CodingDK
CodingDK

Reputation: 163

I know it a bit late.. but I have the same problem.

Here is my solution (my .cshtml-templatepage) (combine of multiple files):

<% for (var chunk of webpack.chunks) {
for (var file of chunk.files) {
    if (file.match(/\.(js|css)$/)) { %>
<link rel="<%= chunk.initial?'preload':'prefetch' %>" href="<%= htmlWebpackPlugin.files.publicPath + file %>" as="<%= file.match(/\.css$/)?'style':'script' %>">
    <% }
    }
}
_.forEach(htmlWebpackPlugin.files.css, function(file) { %>
<link rel="stylesheet" href="<%= file%>" />
<%}); %>

<% _.forEach(htmlWebpackPlugin.files.js, function(file) { %>
<script type="text/javascript" src="<%= file%>"></script>
<%}); %>
  1. First I loop though all files in chucks and look for js/css-files to preload or prefetch.
  2. Next I render my css-files
  3. Finally I render all js-files (this part will not take the prefetched files from part 1, but I don't think it is important for you)

Upvotes: 1

Related Questions