123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- 'use strict';
- const fancyLog = require('fancy-log');
- const PluginError = require('plugin-error');
- const supportsColor = require('supports-color');
- const File = require('vinyl');
- const MemoryFileSystem = require('memory-fs');
- const nodePath = require('path');
- const through = require('through');
- const ProgressPlugin = require('webpack/lib/ProgressPlugin');
- const clone = require('lodash.clone');
- const defaultStatsOptions = {
- colors: supportsColor.stdout.hasBasic,
- hash: false,
- timings: false,
- chunks: false,
- chunkModules: false,
- modules: false,
- children: true,
- version: true,
- cached: false,
- cachedAssets: false,
- reasons: false,
- source: false,
- errorDetails: false
- };
- module.exports = function (options, wp, done) {
- const cache = {
- options: options,
- wp: wp
- };
- options = clone(options) || {};
- let config = options.config || options;
- const isInWatchMode = !!options.watch;
- delete options.watch;
- if (typeof config === 'string') {
- config = require(config);
- }
- // Webpack 4 doesn't support the `quiet` attribute, however supports
- // setting `stats` to a string within an array of configurations
- // (errors-only|minimal|none|normal|verbose) or an object with an absurd
- // amount of config
- const isSilent = options.quiet || (typeof options.stats === 'string' && (options.stats.match(/^(errors-only|minimal|none)$/)));
- if (typeof done !== 'function') {
- let callingDone = false;
- done = function (err, stats) {
- if (err) {
- // The err is here just to match the API but isnt used
- return;
- }
- stats = stats || {};
- if (isSilent || callingDone) {
- return;
- }
- // Debounce output a little for when in watch mode
- if (isInWatchMode) {
- callingDone = true;
- setTimeout(function () {
- callingDone = false;
- }, 500);
- }
- if (options.verbose) {
- fancyLog(stats.toString({
- colors: supportsColor.stdout.hasBasic
- }));
- } else {
- const statsOptions = (options && options.stats) || {};
- if (typeof statsOptions === 'object') {
- Object.keys(defaultStatsOptions).forEach(function (key) {
- if (typeof statsOptions[key] === 'undefined') {
- statsOptions[key] = defaultStatsOptions[key];
- }
- });
- }
- const statusLog = stats.toString(statsOptions);
- if (statusLog) {
- fancyLog(statusLog);
- }
- }
- };
- }
- const webpack = wp || require('webpack');
- let entry = [];
- const entries = Object.create(null);
- const stream = through(function (file) {
- if (file.isNull()) {
- return;
- }
- if ('named' in file) {
- if (!Array.isArray(entries[file.named])) {
- entries[file.named] = [];
- }
- entries[file.named].push(file.path);
- } else {
- entry = entry || [];
- entry.push(file.path);
- }
- }, function () {
- const self = this;
- const handleConfig = function (config) {
- config.output = config.output || {};
- // Determine pipe'd in entry
- if (Object.keys(entries).length > 0) {
- entry = entries;
- if (!config.output.filename) {
- // Better output default for multiple chunks
- config.output.filename = '[name].js';
- }
- } else if (entry.length < 2) {
- entry = entry[0] || entry;
- }
- config.entry = config.entry || entry;
- config.output.path = config.output.path || process.cwd();
- entry = [];
- if (!config.entry || config.entry.length < 1) {
- fancyLog('webpack-stream - No files given; aborting compilation');
- self.emit('end');
- return false;
- }
- return true;
- };
- let succeeded;
- if (Array.isArray(config)) {
- for (let i = 0; i < config.length; i++) {
- succeeded = handleConfig(config[i]);
- if (!succeeded) {
- return false;
- }
- }
- } else {
- succeeded = handleConfig(config);
- if (!succeeded) {
- return false;
- }
- }
- // Cache compiler for future use
- const compiler = cache.compiler || webpack(config);
- cache.compiler = compiler;
- const callback = function (err, stats) {
- if (err) {
- self.emit('error', new PluginError('webpack-stream', err));
- return;
- }
- const jsonStats = stats ? stats.toJson() || {} : {};
- const errors = jsonStats.errors || [];
- if (errors.length) {
- const resolveErrorMessage = (err) => {
- if (
- typeof err === 'object' &&
- err !== null &&
- Object.prototype.hasOwnProperty.call(err, 'message')
- ) {
- return err.message;
- } else if (
- typeof err === 'object' &&
- err !== null &&
- 'toString' in err &&
- err.toString() !== '[object Object]'
- ) {
- return err.toString();
- } else if (Array.isArray(err)) {
- return err.map(resolveErrorMessage).join('\n');
- } else {
- return err;
- }
- };
- const errorMessage = errors.map(resolveErrorMessage).join('\n');
- const compilationError = new PluginError('webpack-stream', errorMessage);
- if (!isInWatchMode) {
- self.emit('error', compilationError);
- }
- self.emit('compilation-error', compilationError);
- }
- if (!isInWatchMode) {
- self.queue(null);
- }
- done(err, stats);
- if (isInWatchMode && !isSilent) {
- fancyLog('webpack is watching for changes');
- }
- };
- if (isInWatchMode) {
- const watchOptions = options.watchOptions || {};
- compiler.watch(watchOptions, callback);
- } else {
- compiler.run(callback);
- }
- const handleCompiler = function (compiler) {
- if (options.progress) {
- (new ProgressPlugin(function (percentage, msg) {
- percentage = Math.floor(percentage * 100);
- msg = percentage + '% ' + msg;
- if (percentage < 10) msg = ' ' + msg;
- fancyLog('webpack', msg);
- })).apply(compiler);
- }
- cache.mfs = cache.mfs || new MemoryFileSystem();
- const fs = compiler.outputFileSystem = cache.mfs;
- const assetEmittedPlugin = compiler.hooks
- // Webpack 4/5
- ? function (callback) { compiler.hooks.assetEmitted.tapAsync('WebpackStream', callback); }
- // Webpack 2/3
- : function (callback) { compiler.plugin('asset-emitted', callback); };
- assetEmittedPlugin(function (outname, _, callback) {
- const file = prepareFile(fs, compiler, outname);
- self.queue(file);
- callback();
- });
- };
- if (Array.isArray(options.config)) {
- compiler.compilers.forEach(function (compiler) {
- handleCompiler(compiler);
- });
- } else {
- handleCompiler(compiler);
- }
- if (options.watch && !isSilent) {
- const watchRunPlugin = compiler.hooks
- // Webpack 4/5
- ? callback => compiler.hooks.watchRun.tapAsync('WebpackInfo', callback)
- // Webpack 2/3
- : callback => compiler.plugin('watch-run', callback);
- watchRunPlugin((compilation, callback) => {
- fancyLog('webpack compilation starting...');
- callback();
- });
- }
- });
- // If entry point manually specified, trigger that
- const hasEntry = Array.isArray(config)
- ? config.some(function (c) { return c.entry; })
- : config.entry;
- if (hasEntry) {
- stream.end();
- }
- return stream;
- };
- function prepareFile (fs, compiler, outname) {
- let path = fs.join(compiler.outputPath, outname);
- if (path.indexOf('?') !== -1) {
- path = path.split('?')[0];
- }
- const contents = fs.readFileSync(path);
- const file = new File({
- base: compiler.outputPath,
- path: nodePath.join(compiler.outputPath, outname),
- contents: contents
- });
- return file;
- }
- // Expose webpack if asked
- Object.defineProperty(module.exports, 'webpack', {
- get: function () {
- return require('webpack');
- }
- });
|