all files / static-site-generator-webpack-plugin/ index.js

100% Statements 79/79
100% Branches 34/34
100% Functions 13/13
100% Lines 79/79
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161   19×     19×   19× 19× 19× 19×     19×   19× 19× 19×   19× 19×   19× 19×   19×     18×   18× 18×   18×     18×     17× 37×           37× 36× 35×       37×       37×   36×   36× 40×             17×             19× 13×   13×     19×   19×     18×   18×     17×     17×       18× 18× 18×     18×       18×   18×     18×     40×   40× 34×     40×     34× 34× 34×                    
var RawSource = require('webpack-sources/lib/RawSource');
var evaluate = require('eval');
var path = require('path');
var Promise = require('bluebird');
 
function StaticSiteGeneratorWebpackPlugin(options) {
  if (arguments.length > 1) {
    options = legacyArgsToOptions.apply(null, arguments);
  }
 
  options = options || {};
 
  this.entry = options.entry;
  this.paths = Array.isArray(options.paths) ? options.paths : [options.paths || '/'];
  this.locals = options.locals;
  this.globals = options.globals;
}
 
StaticSiteGeneratorWebpackPlugin.prototype.apply = function(compiler) {
  var self = this;
 
  compiler.plugin('this-compilation', function(compilation) {
    compilation.plugin('optimize-assets', function(_, done) {
      var renderPromises;
 
      var webpackStats = compilation.getStats();
      var webpackStatsJson = webpackStats.toJson();
 
      try {
        var asset = findAsset(self.entry, compilation, webpackStatsJson);
 
        if (asset == null) {
          throw new Error('Source file not found: "' + self.entry + '"');
        }
 
        var assets = getAssetsFromCompilation(compilation, webpackStatsJson);
 
        var source = asset.source();
        var render = evaluate(source, /* filename: */ self.entry, /* scope: */ self.globals, /* includeGlobals: */ true);
 
        if (render.hasOwnProperty('default')) {
          render = render['default'];
        }
 
        if (typeof render !== 'function') {
          throw new Error('Export from "' + self.entry + '" must be a function that returns an HTML string. Is output.libraryTarget in the configuration set to "umd"?');
        }
 
        renderPromises = self.paths.map(function(outputPath) {
          var locals = {
            path: outputPath,
            assets: assets,
            webpackStats: webpackStats
          };
 
          for (var prop in self.locals) {
            if (self.locals.hasOwnProperty(prop)) {
              locals[prop] = self.locals[prop];
            }
          }
 
          var renderPromise = render.length < 2 ?
            Promise.resolve(render(locals)) :
            Promise.fromNode(render.bind(null, locals));
 
          return renderPromise
            .then(function(output) {
              var outputByPath = typeof output === 'object' ? output : makeObject(outputPath, output);
 
              Object.keys(outputByPath).forEach(function(key) {
                compilation.assets[pathToAssetName(key)] = new RawSource(outputByPath[key]);
              });
            })
            .catch(function(err) {
              compilation.errors.push(err.stack);
            });
        });
 
        Promise.all(renderPromises).nodeify(done);
      } catch (err) {
        compilation.errors.push(err.stack);
        done();
      }
    });
  });
};
 
var findAsset = function(src, compilation, webpackStatsJson) {
  if (!src) {
    var chunkNames = Object.keys(webpackStatsJson.assetsByChunkName);
 
    src = chunkNames[0];
  }
 
  var asset = compilation.assets[src];
 
  if (asset) {
    return asset;
  }
 
  var chunkValue = webpackStatsJson.assetsByChunkName[src];
 
  if (!chunkValue) {
    return null;
  }
  // Webpack outputs an array for each chunk when using sourcemaps
  if (chunkValue instanceof Array) {
    // Is the main bundle always the first element?
    chunkValue = chunkValue[0];
  }
  return compilation.assets[chunkValue];
};
 
// Shamelessly stolen from html-webpack-plugin - Thanks @ampedandwired :)
var getAssetsFromCompilation = function(compilation, webpackStatsJson) {
  var assets = {};
  for (var chunk in webpackStatsJson.assetsByChunkName) {
    var chunkValue = webpackStatsJson.assetsByChunkName[chunk];
 
    // Webpack outputs an array for each chunk when using sourcemaps
    if (chunkValue instanceof Array) {
      // Is the main bundle always the first element?
      chunkValue = chunkValue[0];
    }
 
    if (compilation.options.output.publicPath) {
      chunkValue = compilation.options.output.publicPath + chunkValue;
    }
    assets[chunk] = chunkValue;
  }
 
  return assets;
};
 
function pathToAssetName(outputPath) {
  var outputFileName = outputPath.replace(/^(\/|\\)/, ''); // Remove leading slashes for webpack-dev-server
 
  if (!/\.(html?)$/i.test(outputFileName)) {
    outputFileName = path.join(outputFileName, 'index.html');
  }
 
  return outputFileName;
}
 
function makeObject(key, value) {
  var obj = {};
  obj[key] = value;
  return obj;
}
 
function legacyArgsToOptions(entry, paths, locals, globals) {
  return {
    entry: entry,
    paths: paths,
    locals: locals,
    globals: globals
  };
}
 
module.exports = StaticSiteGeneratorWebpackPlugin;