nwb
Version:
A toolkit for React, Preact & Inferno apps, React libraries and other npm modules for the web, with no configuration (until you need it)
277 lines (217 loc) • 8.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getUserConfig = getUserConfig;
exports.getProjectType = getProjectType;
exports.processUserConfig = processUserConfig;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _chalk = _interopRequireDefault(require("chalk"));
var _webpack2 = _interopRequireDefault(require("webpack"));
var _constants = require("../constants");
var _debug = _interopRequireDefault(require("../debug"));
var _errors = require("../errors");
var _utils = require("../utils");
var _babel = require("./babel");
var _karma = require("./karma");
var _npm = require("./npm");
var _UserConfigReport = _interopRequireDefault(require("./UserConfigReport"));
var _webpack3 = require("./webpack");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const DEFAULT_REQUIRED = false;
/**
* Load a user config file and process it.
*/
function getUserConfig(args = {}, options = {}) {
let {
check = false,
pluginConfig = {},
// eslint-disable-line no-unused-vars
required = DEFAULT_REQUIRED
} = options; // Try to load default user config, or use a config file path we were given
let userConfig = {};
let configPath = _path.default.resolve(args.config || _constants.CONFIG_FILE_NAME); // Bail early if a config file is required by the current command, or if the
// user specified a custom location, and it doesn't exist.
let configFileExists = _fs.default.existsSync(configPath);
if ((args.config || required) && !configFileExists) {
throw new Error(`Couldn't find a config file at ${configPath}`);
} // If a config file exists, it should be a valid module regardless of whether
// or not it's required.
if (configFileExists) {
try {
userConfig = require(configPath);
(0, _debug.default)('imported config module from %s', configPath); // Delete the config file from the require cache as some builds need to
// import it multiple times with a different NODE_ENV in place.
delete require.cache[configPath];
} catch (e) {
throw new Error(`Couldn't import the config file at ${configPath}: ${e.message}\n${e.stack}`);
}
}
userConfig = processUserConfig({
args,
check,
configFileExists,
configPath,
pluginConfig,
required,
userConfig
});
if (configFileExists) {
userConfig.path = configPath;
}
return userConfig;
}
/**
* Load a user config file to get its project type. If we need to check the
* project type, a config file must exist.
*/
function getProjectType(args = {}) {
// Try to load default user config, or use a config file path we were given
let userConfig = {};
let configPath = _path.default.resolve(args.config || _constants.CONFIG_FILE_NAME); // Bail early if a config file doesn't exist
let configFileExists = _fs.default.existsSync(configPath);
if (!configFileExists) {
throw new Error(`Couldn't find a config file at ${configPath} to determine project type.`);
}
try {
userConfig = require(configPath); // Delete the file from the require cache as it may be imported multiple
// times with a different NODE_ENV in place depending on the command.
delete require.cache[configPath];
} catch (e) {
throw new Error(`Couldn't import the config file at ${configPath}: ${e.message}\n${e.stack}`);
} // Config modules can export a function if they need to access the current
// command or the webpack dependency nwb manages for them.
if ((0, _utils.typeOf)(userConfig) === 'function') {
userConfig = userConfig({
args,
command: args._[0],
webpack: _webpack2.default
});
}
let report = new _UserConfigReport.default({
configFileExists,
configPath
});
if (!_constants.PROJECT_TYPES.has(userConfig.type)) {
report.error('type', userConfig.type, `Must be one of: ${(0, _utils.joinAnd)(Array.from(_constants.PROJECT_TYPES), 'or')}`);
}
if (report.hasErrors()) {
throw new _errors.ConfigValidationError(report);
}
return userConfig.type;
}
let warnedAboutPolyfillConfig = false;
/**
* Validate user config and perform any supported transformations to it.
*/
function processUserConfig({
args = {},
check = false,
configFileExists,
configPath,
pluginConfig = {},
required = DEFAULT_REQUIRED,
userConfig
}) {
// Config modules can export a function if they need to access the current
// command or the webpack dependency nwb manages for them.
if ((0, _utils.typeOf)(userConfig) === 'function') {
userConfig = userConfig({
args,
command: args._[0],
webpack: _webpack2.default
});
}
let report = new _UserConfigReport.default({
configFileExists,
configPath
});
let {
type,
browsers,
// TODO Deprecated - remove
polyfill,
babel,
devServer,
karma,
npm,
webpack: _webpack,
// eslint-disable-line no-unused-vars
...unexpectedConfig
} = userConfig;
let unexpectedProps = Object.keys(unexpectedConfig);
if (unexpectedProps.length > 0) {
report.error('nwb config', unexpectedProps.join(', '), `Unexpected top-level prop${(0, _utils.pluralise)(unexpectedProps.length)} in nwb config - ` + 'see https://github.com/insin/nwb/blob/master/docs/Configuration.md for supported config');
}
if ((required || 'type' in userConfig) && !_constants.PROJECT_TYPES.has(type)) {
report.error('type', userConfig.type, `Must be one of: ${(0, _utils.joinAnd)(Array.from(_constants.PROJECT_TYPES), 'or')}`);
}
if ('browsers' in userConfig) {
if ((0, _utils.typeOf)(browsers) === 'string' || (0, _utils.typeOf)(browsers) === 'array') {
userConfig.browsers = {
development: browsers,
production: browsers
};
} else if ((0, _utils.typeOf)(browsers) === 'object') {
if (!browsers.hasOwnProperty('development') && !browsers.hasOwnProperty('production')) {
report.hint('browsers', `You provided ${_chalk.default.cyan('browsers')} config but didn't provide ${_chalk.default.cyan('development')} or ${_chalk.default.cyan('production')} settings`);
}
} else {
report.error('browsers', `type: ${(0, _utils.typeOf)(browsers)}`, `Must be a ${_chalk.default.cyan('String')}, ${_chalk.default.cyan('Array')} or ${_chalk.default.cyan('Object')}`);
}
} // TODO Deprecated - remove
if ('polyfill' in userConfig) {
if (!warnedAboutPolyfillConfig) {
report.deprecated('polyfill', 'Default polyfills were removed in nwb v0.25.0, so polyfill config is no longer supported');
warnedAboutPolyfillConfig = true;
}
}
let argumentOverrides = {};
void ['babel', 'devServer', 'karma', 'npm', 'webpack'].forEach(prop => {
// Set defaults for top-level config objects so we don't have to
// existence-check them everywhere.
if (!(prop in userConfig)) {
userConfig[prop] = {};
} else if ((0, _utils.typeOf)(userConfig[prop]) !== 'object') {
report.error(prop, `type: ${(0, _utils.typeOf)(userConfig[prop])}`, `${_chalk.default.cyan(prop)} config must be an ${_chalk.default.cyan('Object')} `); // Set a valid default so further checks can continue
userConfig[prop] = {};
} // Allow config overrides via --path.to.config in args
if ((0, _utils.typeOf)(args[prop]) === 'object') {
argumentOverrides[prop] = args[prop];
}
});
if (Object.keys(argumentOverrides).length > 0) {
(0, _debug.default)('user config arguments: %s', (0, _utils.deepToString)(argumentOverrides));
userConfig = (0, _utils.replaceArrayMerge)(userConfig, argumentOverrides);
report.hasArgumentOverrides = true;
}
(0, _babel.processBabelConfig)({
report,
userConfig
});
(0, _karma.processKarmaConfig)({
report,
userConfig
});
(0, _npm.processNpmBuildConfig)({
report,
userConfig
});
(0, _webpack3.processWebpackConfig)({
pluginConfig,
report,
userConfig
});
if (report.hasErrors()) {
throw new _errors.ConfigValidationError(report);
}
if (check) {
throw report;
}
if (report.hasSomethingToReport()) {
report.log();
}
(0, _debug.default)('user config: %s', (0, _utils.deepToString)(userConfig));
return userConfig;
}