universal-webpack
Version:
Isomorphic Webpack
360 lines (275 loc) • 15 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = server_configuration;
exports.is_external = is_external;
exports.dont_emit_file_loader = dont_emit_file_loader;
exports.replace_style_loader = replace_style_loader;
exports.extract_package_name = extract_package_name;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _path = _interopRequireDefault(require("path"));
var _util = _interopRequireDefault(require("util"));
var _webpack = _interopRequireDefault(require("webpack"));
var _validateNpmPackageName = _interopRequireDefault(require("validate-npm-package-name"));
var _helpers = require("./helpers.js");
var _loaders = require("./loaders.js");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
// Tunes the client-side Webpack configuration for server-side build
function server_configuration(webpack_configuration, settings) {
// if (!webpack_configuration.context)
// {
// throw new Error(`You must set "context" parameter in your Webpack configuration`)
// }
var configuration = (0, _helpers.clone)(webpack_configuration); // By default, Webpack sets `context` to `process.cwd()`
configuration.context = configuration.context || process.cwd(); // (without extension)
var output_file_name = _path["default"].basename(settings.server.output, _path["default"].extname(settings.server.output));
configuration.entry = (0, _defineProperty2["default"])({}, output_file_name, settings.server.input); // https://webpack.github.io/docs/configuration.html#target
configuration.target = 'node'; // Tell Webpack to leave `__dirname` and `__filename` unchanged
// https://github.com/webpack/webpack/issues/1599#issuecomment-186841345
configuration.node = configuration.node || {};
configuration.node.__dirname = false;
configuration.node.__filename = false; // https://webpack.js.org/configuration/output/#outputlibrarytarget
configuration.output.libraryTarget = 'commonjs2'; // No need for browser cache management, so disable hashes in filenames
configuration.output.filename = '[name].js';
configuration.output.chunkFilename = '[name].js'; // Include comments with information about the modules.
// require(/* ./test */23).
// What for is it here? I don't know. It's a copy & paste from the Webpack author's code.
configuration.output.pathinfo = true; // Output server bundle into its own directory
configuration.output.path = _path["default"].resolve(configuration.context, _path["default"].dirname(settings.server.output)); // Output "*.map" file for human-readable stack traces
configuration.devtool = 'source-map'; // https://webpack.github.io/docs/configuration.html#externals
//
// `externals` allows you to specify dependencies for your library
// that are not resolved by webpack, but become dependencies of the output.
// This means they are imported from the environment during runtime.
//
// So that Webpack doesn't bundle "node_modules" into server.js.
configuration.externals = configuration.externals || [];
if (!Array.isArray(configuration.externals)) {
configuration.externals = [configuration.externals];
}
configuration.externals.push(function (context, request, callback) {
if (is_external(request, configuration, settings)) {
// Resolve dependency as external
return callback(null, request);
} // Resolve dependency as non-external
return callback();
}); // Normalize `modules.rules` loaders.
(0, _loaders.normalize_configuration_rule_loaders)(configuration); // Replace `style-loader` and `css-loader` with `css-loader?exportOnlyLocals=true`
// since it's no web browser and no files will be emitted.
replace_style_loader(configuration); // Add `emit: false` flag to `file-loader` and `url-loader`,
// since there's no need out emit files on the server side
// (can just use the assets emitted on client build
// since the filenames are the same)
dont_emit_file_loader(configuration);
configuration.plugins = configuration.plugins || []; // Remove HotModuleReplacementPlugin and CommonsChunkPlugin
configuration.plugins = configuration.plugins.filter(function (plugin) {
try {
if (plugin.constructor === _webpack["default"].optimize.CommonsChunkPlugin) {
return false;
}
} catch (error) {// Webpack 4 throws `RemovedPluginError`.
}
return plugin.constructor !== _webpack["default"].HotModuleReplacementPlugin;
}); // Add a couple of utility plugins
configuration.plugins = configuration.plugins.concat( // Resorted from using it here because
// if the `build/server` folder is not there
// when Nodemon starts then it simply won't detect
// updates of the server-side bundle
// and therefore won't restart on code changes.
//
// `build/server` folder needs to be present
// by the time Nodemon starts,
// and that's accomplished with a separate npm script.
// // Cleans the output folder
// new clean_plugin([path.dirname(settings.server.output)],
// {
// root: configuration.context
// }),
// Put the resulting Webpack compiled code into a sigle javascript file
// (doesn't disable CommonsChunkPlugin)
new _webpack["default"].optimize.LimitChunkCountPlugin({
maxChunks: 1
})); // Done
return configuration;
} // Checks if a require()d dependency is external.
// Could also use https://www.npmjs.com/package/webpack-node-externals.
// Still the self-made alias-aware solution works ok.
function is_external(request, webpack_configuration, settings) {
// If someone finds a way to mark all assets (jpg, png, css, scss)
// as not external then create a Pull Request on github.
// Until then, all assets from `node_modules` have to be specified
// inside `exclude_from_externals` configuration parameter.
// Mark all files inside packages (e.g. `node_modules`) as external.
var package_name = extract_package_name(request); // Skip webpack loader specific require()d paths
// https://webpack.github.io/docs/loaders.html
if ((0, _helpers.starts_with)(package_name, '!') || (0, _helpers.starts_with)(package_name, '-!')) {
// The dependency is not external
return false;
} // If it's not a module require call,
// then resolve it as non-external.
//
// https://github.com/npm/validate-npm-package-name
//
if (!(0, _validateNpmPackageName["default"])(package_name).validForNewPackages) {
// The dependency is not external
return false;
} // If any aliases are specified, then resolve those aliases as non-external
if (webpack_configuration.resolve && webpack_configuration.resolve.alias) {
for (var _i = 0, _Object$keys = Object.keys(webpack_configuration.resolve.alias); _i < _Object$keys.length; _i++) {
var alias = _Object$keys[_i];
// if (request === key || starts_with(request, key + '/'))
if (package_name === alias) {
// The module is not external
return false;
}
}
} // Allows camelCasing
var exclude_from_externals_extensions = settings.load_external_module_file_extensions || settings.loadExternalModuleFileExtensions || ['css', 'png', 'jpg', 'svg', 'xml']; // Assets are being exluded from externals
// because they need loaders in order to be `require()`d.
var extname = _path["default"].extname(request);
if (extname) {
var extension = extname.slice(1);
if (extension) {
if (exclude_from_externals_extensions.indexOf(extension) >= 0) {
// "The module is not external"
// (which means "load this module with a special loader")
return false;
}
}
} // Allows camelCasing
var exclude_from_externals = settings.exclude_from_externals || settings.excludeFromExternals; // Skip modules explicitly ignored by the user
if (exclude_from_externals) {
var _iterator = _createForOfIteratorHelper(exclude_from_externals),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var exclusion_pattern = _step.value;
if (typeof exclusion_pattern === 'string') {
if (request === exclusion_pattern || (0, _helpers.starts_with)(request, exclusion_pattern + '/')) {
// The module is not external
return false;
}
} else if (exclusion_pattern instanceof RegExp) {
if (exclusion_pattern.test(request)) {
// The module is not external
return false;
}
} else {
throw new Error("Invalid exclusion pattern: ".concat(exclusion_pattern, ". Only strings and regular expressions are allowed."));
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
} // The module is external
return true;
} // Adds `emitFile: false` flag to `file-loader` and `url-loader`,
// since there's no need out emit files on the server side
// (can just use the assets emitted on client build
// since the filenames are the same)
function dont_emit_file_loader(configuration) {
var _iterator2 = _createForOfIteratorHelper(configuration.module.rules),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var rule = _step2.value;
if (rule.oneOf) {
var _iterator3 = _createForOfIteratorHelper(rule.oneOf),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var subrule = _step3.value;
_dont_emit_file_loader(subrule);
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
continue;
}
_dont_emit_file_loader(rule);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
} // Adds `emitFile: false` flag to `file-loader` and `url-loader`,
// since there's no need to emit files on the server side
// (it can just use the assets emitted on client build
// since the filenames are the same)
function _dont_emit_file_loader(rule) {
var file_loader = (0, _loaders.find_loader)(rule, 'file-loader');
var url_loader = (0, _loaders.find_loader)(rule, 'url-loader');
if (file_loader && url_loader) {
throw new Error('You have both "url-loader" and "file-loader" defined for rule which makes no sense', _util["default"].inspect(rule));
}
var loader = file_loader || url_loader;
if (loader) {
loader.options = _objectSpread(_objectSpread({}, loader.options), {}, {
emitFile: false
});
}
} // Replaces `style-loader` and `css-loader` with `css-loader?exportOnlyLocals=true`
// since it's no web browser and no files will be emitted.
function replace_style_loader(configuration) {
var _iterator4 = _createForOfIteratorHelper((0, _loaders.get_style_rules)(configuration)),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var rule = _step4.value;
var css_loader = (0, _loaders.find_loader)(rule, 'css-loader');
if (css_loader) {
var modules = css_loader.options && css_loader.options.modules;
if (modules === undefined || modules === true) {
modules = {};
} else if (typeof modules === 'string') {
modules = {
mode: modules
};
}
if (modules) {
css_loader.options = _objectSpread(_objectSpread({}, css_loader.options), {}, {
modules: _objectSpread(_objectSpread({}, modules), {}, {
exportOnlyLocals: true
})
});
} // Drop `style-loader`.
rule.use = rule.use.filter(function (loader) {
return loader.loader !== 'style-loader';
});
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
} // Extracts npm package name.
// Correctly handles "private" npm packages like `@namespace/package`.
function extract_package_name(path) {
if (path.indexOf('/') === -1) {
return path;
} // For regular npm packages
var package_name = path.slice(0, path.indexOf('/')); // Handle "private" npm packages
if (package_name[0] === '@') {
var start_from = package_name.length + '/'.length;
var to = path.indexOf('/', start_from);
if (to >= 0) {
package_name += path.slice(start_from - '/'.length, to);
} else {
package_name = path;
}
}
return package_name;
}
//# sourceMappingURL=serverConfiguration.js.map