带有 express-handlebars 的 webpack

webpack with express-handlebars

我正在尝试将一个旧的 express 站点迁移到 webpack,但不想修改所有使用 express-handlebars 的布局模板。是否可以将 express-handlebars 与 webpack 一起使用?

handlebars-loader 似乎不支持 express-handlebars 的布局概念,所以在这里没有帮助。

自定义加载程序似乎可以解决问题:

快速车把-loader.js:

const loaderUtils = require('loader-utils');
const validateOptions = require('schema-utils');
const path = require('path');
const express = require('express');
const exphbs = require('express-handlebars');

module.exports = function (content) {
    const options = loaderUtils.getOptions(this);
    const app = options.app;
    const contextCallback = options.contextCallback;
    const view = path.relative(options.basePath, this.resourcePath);
    const context = contextCallback(this.resourcePath, view);
    
    var loaderAsyncCallback = this.async();
    app.render(view, context, function (err, html) {
        if (err) {
            return loaderAsyncCallback(err);
        }
        
        const slug = 
            '// Module\n'
            + 'var code = ' + JSON.stringify(html) + ';\n'
            + '// Exports\n'
            + 'module.exports = code;'
        
        loaderAsyncCallback(null, slug);
    });
  
};

webpack.config.js:

const CopyPlugin = require("copy-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const fs = require('fs');
const url = require('url');
const express = require('express');
const exphbs = require('express-handlebars');

const app = express();

// Handlebars Setup

/**
 * Instantiate a Handlebars instance with our config (default layout, helpers, etc.)
 */
const handlebasInstance = exphbs.create({
  defaultLayout: 'mainLayout',
  // Specify helpers which are only registered on this instance.
  helpers
});

app.engine('handlebars', handlebasInstance.engine);
app.set('view engine', 'handlebars');
app.use('/assets', express.static('assets'));

const basePath = path.resolve(__dirname, './views');

function generateHtmlPlugins(templateDir) {
  const itemList = fs.readdirSync(templateDir);
  return itemList.flatMap(item => {
    const [ name, extension ] = item.split('.');
    if (extension == 'handlebars') {
        const templatePath = path.resolve(templateDir, item);
        const outputPath = path.resolve(templateDir, name + '.html');
        const outputName = path.relative(basePath, outputPath);
        return new HtmlWebpackPlugin({
          filename: outputName,
          inject: false,
          template: templatePath
        })
    } else {
        return [];
    }
  })
}

const siteHtmlPlugins = generateHtmlPlugins(basePath);

function contextCallback(resourcePath, view) {
    var context = {};
    if (view.includes('documentation/')) {
        context.layout = 'documentationLayout';
    }
    return context;
}


module.exports = {
  mode: 'development',
  resolveLoader: {
    modules: [ 'node_modules', path.resolve(__dirname, 'loaders') ]
  },
  entry: './src/entry-workaround.js',
  output: {
    filename: 'entry-workaround.js',
    path: path.resolve(__dirname, 'public'),
  },
  module: {
    rules: [{
        test: /\.handlebars$/,
        loader: "express-handlebars-loader",
        options: {
            app: app,
            basePath: basePath,
            contextCallback: contextCallback,
        }
    }]
  },
  plugins: []
};