Esempio n. 1
0
module.exports = (options = {}) => {
  webpackLog({ name: 'config', id: 'webpack-config-loader' });

  const name = 'config-loader';
  const raw = load(options);

  return resolve(raw).then((result) => {
    const schema = merge({}, options.schema, webpackSchema);
    for (const target of [].concat(result.config)) {
      validate({ name, schema, target });
    }

    return result;
  });
};
Esempio n. 2
0
function createLogger(options) {
  let level = options.logLevel || 'info';

  if (options.noInfo === true) {
    level = 'warn';
  }

  if (options.quiet === true) {
    level = 'silent';
  }

  return log({
    name: 'wds',
    level,
    timestamp: options.logTime,
  });
}
Esempio n. 3
0
module.exports = (options) => {
  const log = weblog({ name: 'serve', id: 'webpack-serve' });
  const bus = nanobus();

  if (isPlainObject(options.on)) {
    for (const event of Object.keys(options.on)) {
      const fn = options.on[event];

      if (typeof fn === 'function') {
        log.info(`Subscribed to '${event}' event`);
        bus.on(event, fn);
      } else {
        throw new WebpackServeError(`The value for an \`on\` event handler must be a Function. event: ${event}`);
      }
    }
  } else if (options.on) {
    throw new WebpackServeError('The value for the `on` option must be an Object. Please see the README.');
  }

  return bus;
};
Esempio n. 4
0
  // open: { app: <String>, path: <String> }
  port: 8080,
  protocol: 'http',
};

module.exports = {
  apply(argv, configs, opts) {
    const flags = applyFlags(argv);
    const [first] = configs;
    const options = merge({}, defaults, opts, flags, first.serve);
    const { https } = options;
    const { hotClient } = options;

    weblog({
      id: 'webpack-serve',
      level: options.logLevel,
      name: 'serve',
      timestamp: options.logTime,
    });

    if (typeof options.content === 'string') {
      options.content = [options.content];
    }

    if (!options.content || !options.content.length) {
      // if no context was specified in a config, and no --content options was
      // used, then we need to derive the context, and content location, from
      // the compiler.
      if (first.context) {
        options.content = [first.context];
      } else {
        options.content = [process.cwd()];
Esempio n. 5
0
module.exports = (options = {}) => {
  const log = webpackLog({ name: 'config', id: 'webpack-config-loader' });
  const requires = [].concat(options.require).filter((r) => !!r);

  // eslint-disable-next-line no-param-reassign
  options = Object.assign({ cwd: process.cwd() }, options);

  for (const module of requires) {
    try {
      const modulePath = resolvePath(module, { basedir: cwd });

      log.info(chalk`Requiring the {cyan ${module}} module`);

      if (options.requireOptions) {
        const { requireOptions } = options;
        // eslint-disable-next-line import/no-dynamic-require, global-require
        require(modulePath)(requireOptions);
      } else {
        // eslint-disable-next-line import/no-dynamic-require, global-require
        require(modulePath);
      }
    } catch (e) {
      log.error(chalk`An error occurred while requiring: {grey ${module}}`);
      throw new RequireModuleError(e, module);
    }
  }

  let config = {};
  let { configPath } = options;

  const explorer = cosmiconfig(prefix, cosmicOptions);

  try {
    let result;
    if (configPath) {
      result = explorer.loadSync(configPath) || {};
    } else {
      result = explorer.searchSync(options.cwd) || {};
    }

    ({ config, filepath: configPath } = result);

    log.debug(chalk`Found config at {grey ${configPath}}`);
  } catch (e) {
    /* istanbul ignore else */
    if (configPath) {
      log.error(chalk`An error occurred while trying to load {grey ${configPath}}
              Did you forget to specify a --require?`);
    } else {
      log.error(chalk`An error occurred while trying to find a config file
              Did you forget to specify a --require?`);
    }
    throw new LoadConfigError(e, configPath);
  }

  if (!configPath && !options.allowMissing) {
    // prettier-ignore
    log.error(chalk`Unable to load a config from: {grey ${options.cwd}}`);
    const e = new Error(`Unable to load a config from: ${options.cwd}`);
    throw new LoadConfigError(e, configPath);
  } else {
    config = config || {};
    configPath = configPath || '';
  }

  return { config, configPath };
};
Esempio n. 6
0
module.exports = (options) => {
  const app = new Koa();
  const { bus } = options;
  const log = weblog({ name: 'serve', id: 'webpack-serve' });
  let http2;
  let server;
  let koaMiddleware;

  const middleware = {
    webpack: () => {
      middleware.webpack.called = true;

      // track the promise state in the event that someone calls server.close()
      // quickly, before this promise resolves.

      try {
        koaMiddleware = koaWebpack({
          // be explicit about what we want to pass, rather than passing the entire
          // options object
          compiler: options.compiler,
          dev: options.dev,
          hot: options.hot,
        });
        app.use(koaMiddleware);
        middleware.webpack.state = Promise.resolve();
      } catch (e) {
        /* istanbul ignore next*/
        middleware.webpack.state = Promise.reject(e);
        /* istanbul ignore next*/
        throw new WebpackServeError(
          `An error was thrown while initializing koa-webpack\n ${e.toString()}`
        );
      }
    },
    content: (staticOptions) => {
      middleware.content.called = true;
      for (const dir of options.content) {
        app.use(serveStatic(dir, staticOptions || {}));
      }
    },
  };

  if (options.http2) {
    // the check for if we can do this is in options.js
    http2 = require('http2'); // eslint-disable-line
  }

  if (options.https) {
    if (http2) {
      server = http2.createSecureServer(options.https, app.callback());
    } else {
      server = https.createServer(options.https, app.callback());
    }
  } else {
    server = (http2 || http).createServer(app.callback());
  }

  killable(server);

  server.on('error', (err) => log.error(err));

  return {
    server,
    close(callback) {
      server.kill(() => {
        if (middleware.webpack.called) {
          middleware.webpack.state.then(() => koaMiddleware.close(callback));
        }
      });
    },
    start() {
      server.once('listening', () => {
        const uri = `${options.protocol}://${options.host}:${options.port}`;

        log.info(chalk`Project is running at {blue ${uri}}`);

        if (options.clipboard && !options.open) {
          /* istanbul ignore next*/
          try {
            clip.writeSync(uri);
            log.info(chalk.grey('Server URI copied to clipboard'));
          } catch (error) {
            log.warn(
              chalk.grey(
                'Failed to copy server URI to clipboard. ' +
                  "Use logLevel: 'debug' for more information."
              )
            );
            log.debug(error);
          }
        }

        bus.emit('listening', { server, options });

        if (options.open) {
          const open = require('opn'); // eslint-disable-line global-require
          open(urljoin(uri, options.open.path || ''), options.open.app || {});
        }
      });

      return Promise.all([
        getPort({ port: options.port, host: options.host }),
        getPort({ port: options.hot.port || 8081, host: options.host }),
      ]).then(([port, hotPort]) => {
        /* eslint-disable no-param-reassign */
        options.port = port;
        if (options.hot) {
          options.hot.port = hotPort;
        }

        if (typeof options.add === 'function') {
          options.add(app, middleware, options);
        }

        if (!middleware.content.called) {
          middleware.content();
        }

        // allow consumers to specifically order middleware
        if (!middleware.webpack.called) {
          middleware.webpack();
        }

        server.listen(options.port, options.host);

        return server;
      });
    },
  };
};
Esempio n. 7
0
    .then((results) => {
      const { options, configs } = results;
      const log = weblog({ name: 'serve', id: 'webpack-serve' });

      options.bus = eventbus(options);
      const { bus } = options;

      if (!options.compiler) {
        try {
          options.compiler = webpack(configs.length > 1 ? configs : configs[0]);
        } catch (e) {
          throw new WebpackServeError(
            `An error was thrown while initializing Webpack\n  ${e.toString()}`
          );
        }
      }

      const compilers = options.compiler.compilers || [options.compiler];
      for (const comp of compilers) {
        comp.hooks.compile.tap('WebpackServe', () => {
          bus.emit('build-started', { compiler: comp });
        });

        comp.hooks.done.tap('WebpackServe', (stats) => {
          const json = stats.toJson();
          if (stats.hasErrors()) {
            bus.emit('compiler-error', { json, compiler: comp });
          }

          if (stats.hasWarnings()) {
            bus.emit('compiler-warning', { json, compiler: comp });
          }

          bus.emit('build-finished', { stats, compiler: comp });
        });
      }

      const { close, server, start } = getServer(options);

      start(options);

      let closing = false;
      const exit = (signal) => {
        if (!closing) {
          closing = true;
          close(() => {
            log.info(`Process Ended via ${signal}`);
            server.kill();
            process.exit(0);
          });
        }
      };

      for (const signal of ['SIGINT', 'SIGTERM']) {
        process.on(signal, () => exit(signal));
      }

      process.on('exit', exit);

      return Object.freeze({
        close,
        compiler: options.compiler,
        on(...args) {
          options.bus.on(...args);
        },
        options,
      });
    })
Esempio n. 8
0
'use strict';

const isPlainObject = require('lodash/isPlainObject');
const TimeFixPlugin = require('time-fix-plugin');
const weblog = require('webpack-log');

module.exports = {

  fromFunction(config, argv) {
    if (typeof config === 'function') {
      const log = weblog({ name: 'serve', id: 'webpack-serve' });
      log.warn('It is not recommended to use configs which export a function. Please see the README for more information.');
      return config('development', argv);
    }

    return config;
  },

  // adds https://github.com/egoist/time-fix-plugin if not already added
  // to the config.
  timeFix(config) {
    if (config.plugins) {
      let timeFixFound = false;
      for (const plugin of config.plugins) {
        if (!timeFixFound && plugin instanceof TimeFixPlugin) {
          timeFixFound = true;
        }
      }

      if (!timeFixFound) {
        config.plugins.unshift(new TimeFixPlugin());
Esempio n. 9
0
function run() {
  /* eslint-disable global-require */
  const { existsSync: exists, statSync: stat } = require('fs');
  const { sep } = require('path');
  const { inspect } = require('util');

  const chalk = require('chalk');
  const meow = require('meow');

  const woof = require('./');
  const { help: commandHelp, load: getCommands } = require('./commands');
  const { help: flagHelp, opts } = require('./flags');
  /* eslint-enable global-require */

  const flagOpts = { flags: opts() };
  const log = weblog({ name: 'command', id: 'webpack-command-forced' });
  const cli = meow(
    chalk`
{underline Usage}
  $ webpack [<config>, ...options]
  $ webpack <entry-file> [...<entry-file>] <output-file>

{underline Options}
${flagHelp()}

  For further documentation, visit {blue https://webpack.js.org/api/cli}

{underline Commands}
${commandHelp()}

  Type \`webpack help <command>\` for more information

{underline Examples}
  $ webpack
  $ webpack --help
  $ webpack entry.js
  $ webpack --config ../webpack.config.js
`,
    flagOpts
  );

  const commands = getCommands();
  const [command] = cli.input;

  cli.argv = cli.flags;
  cli.commands = commands;
  cli.entries = [];

  const cmd = cli.commands[command];

  if (cmd) {
    try {
      cmd.run(cli);
    } catch (e) {
      log.error(chalk`The {bold \`${command}\`} command threw an error:`);
      throw e;
    }
  } else {
    if (cli.input.length) {
      const problems = [];
      const isDir = (path) => stat(path).isDirectory();
      const entries = [];

      for (let file of cli.input) {
        if (!exists(file)) {
          problems.push(file);
        } else {
          if (isDir(file)) {
            file += sep;
          }

          entries.push(file);
        }
      }

      if (problems.length) {
        const prefix =
          problems.length === cli.input.length ? 'The' : 'Some of the';
        const message = `${prefix} input provided did not match any known commands or existing files:
            ${problems.join(' ')}`;
        log.error(message);

        /* istanbul ignore else */
        if (process.env.CLI_TEST === 'true') {
          throw new Error(message);
        } else {
          // citing: http://www.tldp.org/LDP/abs/html/exitcodes.html
          process.exit(127);
        }
      }

      cli.entries = entries;
    }

    woof(cli).catch((e) => {
      let error = e;
      const errorType = error.constructor.name;
      const epilogue = chalk`: {dim use --log-level=debug for more error detail}`;
      let preamble = 'A webpack error occured while building';
      let logStack = true;

      // NOTE: none of these conditionals need coverage. the catch has coverage
      //       and that's what is important

      /* istanbul ignore if */
      if (!(error instanceof Error)) {
        error = new Error(
          `webpack-command failed with a value of: ${inspect(error)}`
        );
      }

      // Assume that errors are generated by webpack first: e.g. ModuleNotFoundError
      // If it's a WebpackCommandError then we goofed somehow.
      // If it's a generic Error it's likely that webpack is throwing a poorly
      // managed error, or a plugin has done something naughty.
      /* istanbul ignore if|else */
      if (errorType === 'WebpackCommandError') {
        preamble = 'An error occured while running webpack-command';
      } else if (errorType === 'Error') {
        /* istanbul ignore next */
        preamble = 'An error occured while running webpack';
      } else {
        logStack = false;
      }

      log.error(preamble + epilogue);

      /* istanbul ignore if */
      if (logStack) {
        log.error(error.stack);
      } else {
        log.error(error.message);
      }

      /* istanbul ignore if */
      if (cli.flags.logLevel === 'debug') {
        log.level = cli.flags.logLevel;
      }

      log.debug(inspect(error));

      process.exitCode = 1;
    });
  }
}
Esempio n. 10
0
  return resolve(opts).then((configs) => {
    const [first] = configs;
    const options = merge({}, defaults, opts, flags, configs[0].serve);
    const https = pull(flags, 'https');
    const open = pull(flags, 'open');

    if (https) {
      options.https = https;
    }

    // separate logical block so that protocol will be set correctly whether
    // https is set via options in config or cli flag
    if (options.https) {
      options.protocol = 'https';
    }

    /* istanbul ignore if */
    if (open) {
      if (!open.path) {
        open.path = '/';
      }
      options.open = open;
    }

    if (typeof options.content === 'string') {
      options.content = [options.content];
    }

    if (!options.content || !options.content.length) {
      if (first.context) {
        options.content = [].concat(first.context);
      }

      options.content.push(process.cwd());
    }

    /* istanbul ignore if */
    if (options.http2 && nodeVersion < 9) {
      throw new TypeError('webpack-serve: The `http2` option can only be used with Node v9 and higher.');
    }

    if (flags.dev && typeof flags.dev === 'string') {
      options.dev = parse(flags.dev);

      if (typeof options.dev !== 'object') {
        throw new TypeError('webpack-serve: The --dev flag must be a string contianing a valid JSON object.');
      }
    }

    if (flags.hot && typeof flags.hot === 'string') {
      options.hot = parse(flags.hot);

      if (typeof options.hot !== 'object') {
        throw new TypeError('webpack-serve: The --hot flag must be a string contianing a valid JSON object.');
      }
    }

    if (flags.hotClient === false) {
      options.hot = false;
    } else {
      /* istanbul ignore if */
      if (flags.hot === false) {
        options.hot = Object.assign({}, options.hot, { hot: false });
      }

      /* istanbul ignore if */
      if (flags.reload === false) {
        options.hot = Object.assign({}, options.hot, { reload: false });
      }
    }

    // because you just know someone is going to do this
    if (options.hot === true) {
      options.hot = defaults.hot;
    }

    if (options.hot) {
      if (options.hot.host) {
        const hotHost = options.hot.host.server || options.hot.host;
        if (hotHost !== options.host) {
          throw new TypeError('webpack-serve: The `hot.host` (or `hot.host.server`) property must match the `host` option.');
        }
      } else {
        options.hot.host = options.host;
      }
    }

    /* istanbul ignore if */
    if (flags.openApp) {
      options.open = Object.assign({}, options.open, {
        app: parse(flags.openApp)
      });
    }

    /* istanbul ignore if */
    if (flags.openPath) {
      options.open = Object.assign({}, options.open, {
        path: flags.openPath
      });
    }

    if (flags.logLevel) {
      options.logLevel = flags.logLevel;
      options.dev = Object.assign(options.dev, { logLevel: options.logLevel });
      options.hot = Object.assign(options.hot, { logLevel: options.logLevel });
    }

    if (flags.logTime) {
      options.logTime = true;
      options.dev = Object.assign(options.dev, { logTime: true });
      options.hot = Object.assign(options.hot, { logTime: true });
    }

    // initialize the shared log
    weblog({
      name: 'serve',
      id: 'webpack-serve',
      level: options.logLevel,
      timestamp: options.logTime
    });

    // cleanup - doing this here so as not to mutate the options passed in.
    delete options.config;

    // this isn't part of the webpack options schema, and doesn't need to be
    delete configs[0].serve;

    return { configs, options };
  });
Esempio n. 11
0
        } else {
          // eslint-disable-next-line valid-typeof
          result = typeof value === type;
        }
      }
    }

    return result;
  },

  validate(opts) {
    const defaults = { throw: true };
    const options = Object.assign({}, defaults, opts);
    const errors = [];
    const { argv, flags, prefix = 'webpack' } = options;
    const log = weblog({ name: prefix, id: `${prefix}-cli-util` });
    const names = Object.keys(flags);
    const tableOptions = {
      align: ['', 'l', 'l'],
      stringLength(str) {
        return strip(str).length;
      },
    };
    const aliases = names
      .map((name) => flags[name].alias)
      .filter((alias) => !!alias);
    const uniqueArgs = new Set(
      Object.keys(argv)
        .map((arg) => decamel(arg, '-'))
        .filter((arg) => !aliases.includes(arg))
    );