universal-webpack
Version:
Isomorphic Webpack
336 lines (264 loc) • 14.3 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
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) { _defineProperty(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; }
import path from 'path';
import util from 'util';
import webpack from 'webpack';
import validate_npm_package_path from 'validate-npm-package-name';
import { clone, starts_with, ends_with } from './helpers.js';
import { find_loader, get_style_rules, normalize_configuration_rule_loaders } from './loaders.js'; // Tunes the client-side Webpack configuration for server-side build
export default function server_configuration(webpack_configuration, settings) {
// if (!webpack_configuration.context)
// {
// throw new Error(`You must set "context" parameter in your Webpack configuration`)
// }
var configuration = clone(webpack_configuration); // By default, Webpack sets `context` to `process.cwd()`
configuration.context = configuration.context || process.cwd(); // (without extension)
var output_file_name = path.basename(settings.server.output, path.extname(settings.server.output));
configuration.entry = _defineProperty({}, 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.resolve(configuration.context, path.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.
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.optimize.CommonsChunkPlugin) {
return false;
}
} catch (error) {// Webpack 4 throws `RemovedPluginError`.
}
return plugin.constructor !== webpack.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.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.
export 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 (starts_with(package_name, '!') || 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 (!validate_npm_package_path(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.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 || 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)
export 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 = find_loader(rule, 'file-loader');
var url_loader = 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.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.
export function replace_style_loader(configuration) {
var _iterator4 = _createForOfIteratorHelper(get_style_rules(configuration)),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var rule = _step4.value;
var css_loader = 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`.
export 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=server configuration.js.map