UNPKG

webpack-isomorphic-tools

Version:
670 lines (537 loc) 21.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _getIterator2 = require('babel-runtime/core-js/get-iterator'); var _getIterator3 = _interopRequireDefault(_getIterator2); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _stringify = require('babel-runtime/core-js/json/stringify'); var _stringify2 = _interopRequireDefault(_stringify); exports.default = write_assets; exports.extract_path = extract_path; var _fsExtra = require('fs-extra'); var _fsExtra2 = _interopRequireDefault(_fsExtra); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _semver = require('semver'); var _semver2 = _interopRequireDefault(_semver); var _requireHacker = require('require-hacker'); var _requireHacker2 = _interopRequireDefault(_requireHacker); var _serializeJavascript = require('../tools/serialize-javascript'); var _serializeJavascript2 = _interopRequireDefault(_serializeJavascript); var _helpers = require('../helpers'); var _common = require('../common'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // one can supply a custom filter var default_filter = function default_filter(module, regular_expression) { return regular_expression.test(module.name); }; // one can supply a custom namer var default_asset_path = function default_asset_path(module) { return module.name; }; // one can supply a custom parser var default_parser = function default_parser(module) { return module.source; }; // writes webpack-assets.json file, which contains assets' file paths function write_assets(json, options, log) { // take the passed in options options = (0, _helpers.clone)(options); log.debug('running write assets webpack plugin v' + require('../../package.json').version + ' with options', options); // make webpack stats accessible for asset functions (parser, path, filter) options.webpack_stats = json; var development = options.development; if (development) { log.debug(' (development mode is on)'); } // write webpack stats json for debugging purpose if (options.debug) { // write webpack stats file log.debug('writing webpack stats to ' + options.webpack_stats_path); // write the file // (format the JSON for better readability) _fsExtra2.default.outputFileSync(options.webpack_stats_path, (0, _stringify2.default)(json, null, 2)); } // Note Webpack version due to breaking changes between v2 and v3. // https://github.com/catamphetamine/webpack-isomorphic-tools/issues/142 // // Convert things like `3.0.0-alpha.1` to just `3.0.0`. // var version = new _semver2.default(json.version); version = version.major + '.' + version.minor + '.' + version.patch; options.webpackVersion = version; options.webpackUsesTildeForNodeModules = (0, _common.webpack_uses_tilde_for_node_modules)(version); // the output object with assets var output = options.output; output.webpack = { version: version // populate the output object with assets };populate_assets(output, json, options, log); // write webpack assets info file if (options.output_to_a_file) { // format the JSON for better readability if in debug mode var assets_info = development ? (0, _stringify2.default)(output, null, 2) : (0, _stringify2.default)(output); // rewrite `webpack-assets.json` var rewrite = true; // for `webpack-assets.json` caching to work // chunks info should be moved out of it, // otherwise chunk hashsums constantly change, // and there won't be any caching. // // const assets_buffer = Buffer.from(assets_info) // // // if webpack-assets.json already exists, // // then maybe no need to rewrite it // if (fs.existsSync(options.webpack_assets_path)) // { // // previously written webpack-assets.json // const previous_assets_buffer = fs.readFileSync(options.webpack_assets_path) // // // if webpack-assets.json rewrite is not needed, then don't do it // if (assets_buffer.equals(previous_assets_buffer)) // { // rewrite = false // } // } // if webpack-assets.json rewrite is needed, then do it if (rewrite) { log.debug('writing webpack assets info to ' + options.webpack_assets_path); // write the file _fsExtra2.default.outputFileSync(options.webpack_assets_path, assets_info); } } else { log.debug('serving webpack assets from memory'); } // return Webpack assets JSON object // for serving it through HTTP service return output; } // populates the output object with assets function populate_assets(output, json, options, log) { // for each chunk name ("main", "common", ...) (0, _keys2.default)(json.assetsByChunkName).forEach(function (name) { log.debug('getting javascript and styles for chunk "' + name + '"'); // get javascript chunk real file path var javascript = get_assets(name, 'js')[0]; // the second asset is usually a source map if (javascript) { log.debug(' (got javascript)'); output.javascript[name] = javascript; } // get style chunk real file path var style = get_assets(name, 'css')[0]; // the second asset is usually a source map if (style) { log.debug(' (got style)'); output.styles[name] = style; } }); // gets asset paths by name and extension of their chunk function get_assets(name) { var extension = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'js'; var chunk = json.assetsByChunkName[name]; // a chunk could be a string or an array, so make sure it is an array if (!Array.isArray(chunk)) { chunk = [chunk]; } return chunk // filter by extension .filter(function (name) { return _path2.default.extname(extract_path(name)) === '.' + extension; }) // adjust the real path (can be http, filesystem) .map(function (name) { return options.assets_base_url + name; }); } // 1st pass var parsed_assets = {}; // global paths to parsed asset paths var global_paths_to_parsed_asset_paths = {}; // define __webpack_public_path__ webpack variable // (resolves "ReferenceError: __webpack_public_path__ is not defined") var define_webpack_public_path = 'var __webpack_public_path__ = ' + (0, _stringify2.default)(options.assets_base_url) + ';\n'; // for each user specified asset type var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { var _loop = function _loop() { var asset_type = _step.value; // asset type settings var asset_type_settings = options.assets[asset_type]; // one can supply his own filter var filter = asset_type_settings.filter || default_filter; //.bind(this) // one can supply his own path parser var extract_asset_path = asset_type_settings.path || default_asset_path; //.bind(this) // one can supply his own parser var parser = asset_type_settings.parser || default_parser; //.bind(this) // guard agains typos, etc // for filter if (!asset_type_settings.filter) { log.debug('No filter specified for "' + asset_type + '" assets. Using a default one.'); } // for path parser if (!asset_type_settings.path) { log.debug('No path parser specified for "' + asset_type + '" assets. Using a default one.'); } // for parser if (!asset_type_settings.parser) { log.debug('No parser specified for "' + asset_type + '" assets. Using a default one.'); } log.debug('parsing assets of type "' + asset_type + '"'); // timer start var began_at = new Date().getTime(); // get real paths for all the files from this asset type json.modules // take just modules of this asset type .filter(function (module) { // check that this asset is of the asset type if (!filter(module, options.regular_expressions[asset_type], options, log)) { return false; } // guard against an empty source. if (!module.source) { log.error('Module "' + module.name + '" has no source. Maybe Webpack compilation of this module failed. Skipping this asset.'); return false; } // include this asset return true; }).reduce(function (set, module) { // determine asset real path var asset_path = extract_asset_path(module, options, log); // asset module source, or asset content (or whatever else) var parsed_asset = parser(module, options, log); log.trace('Adding asset "' + asset_path + '", module id ' + module.id + ' (in webpack-stats.json)'); // check for naming collisions (just in case) if ((0, _helpers.exists)(set[asset_path])) { log.error('-----------------------------------------------------------------'); log.error('Asset with path "' + asset_path + '" was overwritten because of path collision.'); log.error('Use the "filter" function of this asset type to narrow the results.'); log.error('Previous asset with this path:'); log.error(set[asset_path]); log.error('New asset with this path:'); log.error(parsed_asset); log.error('-----------------------------------------------------------------'); } // add this asset to the list // // also resolve "ReferenceError: __webpack_public_path__ is not defined". // because it may be a url-loaded resource (e.g. a font inside a style). set[asset_path] = define_webpack_public_path + _requireHacker2.default.to_javascript_module_source(parsed_asset); // add path mapping global_paths_to_parsed_asset_paths[_path2.default.resolve(options.project_path, asset_path)] = asset_path; // continue return set; }, parsed_assets); // timer stop log.debug(' time taken: ' + (new Date().getTime() - began_at) + ' ms'); }; for (var _iterator = (0, _getIterator3.default)((0, _keys2.default)(options.assets)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { _loop(); } // register a special require() hook for requiring() raw webpack modules } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var require_hook = _requireHacker2.default.global_hook('webpack-module', function (required_path, module) { log.debug('require()ing "' + required_path + '"'); // if Webpack aliases are supplied if (options.alias) { // possibly alias the path var aliased_global_path = (0, _common.alias_hook)(required_path, module, options.project_path, options.alias, log); // if an alias is found if (aliased_global_path) { var result = { source: _requireHacker2.default.to_javascript_module_source(safe_require(aliased_global_path, log)), path: aliased_global_path }; return result; } } // If the `required_path` is an npm package path // still can't simply `return` and fall back to default Node.js behaviour // because it could still be an asset like // `require('bootstrap/style.css')` inside `./style.css`. // // If the `required_path` is not an asset though // (which means "has no extension" in this case — should work) // then `require()` it as usual. // if (required_path.split(_path2.default.sep).slice(-1)[0].indexOf('.') === -1) { return; } // find an asset with this path // // the require()d path will be global path in case of the for..of require() loop // for the assets (the code a couple of screens below). // // (it can be anything in other cases (e.g. nested require() calls from the assets)) // if ((0, _helpers.exists)(global_paths_to_parsed_asset_paths[required_path])) { log.debug(' found in parsed assets'); var _result = { source: parsed_assets[global_paths_to_parsed_asset_paths[required_path]], path: required_path }; return _result; } log.debug(' not found in parsed assets, searching in webpack stats'); // find a webpack module which has a reason with this path var candidates = []; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = (0, _getIterator3.default)(json.modules), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _module = _step2.value; var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = (0, _getIterator3.default)(_module.reasons), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var reason = _step4.value; if (reason.userRequest === required_path) { candidates.push(_module); break; } } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } } // guard against ambiguity } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } if (candidates.length === 1) { log.debug(' found in webpack stats, module id ' + candidates[0].id); // also resolve "ReferenceError: __webpack_public_path__ is not defined". // because it may be a url-loaded resource (e.g. a font inside a style). var _result2 = { source: define_webpack_public_path + candidates[0].source, path: candidates[0].identifier }; return _result2; } // if there are more than one candidate for this require()d path, // then try to guess which one is the one require()d if (candidates.length > 1) { log.debug(' More than a single candidate module was found in webpack stats for require()d path "' + required_path + '"'); var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = (0, _getIterator3.default)(candidates), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var candidate = _step3.value; log.debug(' ', candidate); } // (loaders matter so the program can't simply throw them away from the required path) // // // tries to normalize a cryptic Webpack loader path // // into a regular relative file path // // https://webpack.github.io/docs/loaders.html // let filesystem_required_path = last(required_path // .replace(/^!!/, '') // .replace(/^!/, '') // .replace(/^-!/, '') // .split('!')) } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } var fail = function fail() { throw new Error('More than a single candidate module was found in webpack stats for require()d path "' + required_path + '". Enable "debug: true" flag in webpack-isomorphic-tools configuration for more info.'); }; // https://webpack.github.io/docs/loaders.html var is_webpack_loader_path = required_path.indexOf('!') >= 0; // if it's a Webpack loader-powered path, the code gives up if (is_webpack_loader_path) { fail(); } // from here on it's either a filesystem path or an npm module path // if it's a global path it can be resolved right away if ((0, _common.is_global_path)(required_path)) { var _result3 = { source: _requireHacker2.default.to_javascript_module_source(safe_require(required_path, log)), path: required_path }; return _result3; } // from here on it's either a relative filesystem path or an npm module path, // so it can be resolved against the require()ing file path (if it can be recovered). // `module.filename` here can be anything, not just a filesystem absolute path, // since some advanced require() hook trickery is involved. // therefore it will be parsed. // var requiring_file_path = module.filename.replace(/\.webpack-module$/, ''); // if it's a webpack loader-powered path, then extract the filesystem path from it if (requiring_file_path.indexOf('!') >= 0) { requiring_file_path = requiring_file_path.substring(requiring_file_path.lastIndexOf('!') + 1); } // make relative path global if ((0, _common.is_relative_path)(requiring_file_path)) { requiring_file_path = _path2.default.resolve(options.project_path, requiring_file_path); } // if `requiring_file_path` is a filesystem path (not an npm module path), // then the require()d path can possibly be resolved if ((0, _common.is_global_path)(requiring_file_path)) { log.debug(' The module is being require()d from "' + requiring_file_path + '", so resolving the path against this file'); // if it's a relative path, can try to resolve it if ((0, _common.is_relative_path)(required_path)) { var absolute_path = _path2.default.resolve(requiring_file_path, '..', required_path); var _result4 = { source: _requireHacker2.default.to_javascript_module_source(safe_require(absolute_path, log)), path: absolute_path }; return _result4; } // if it's an npm module path (e.g. 'babel-runtime/core-js/object/assign'), // can try to require() it from the requiring asset path if ((0, _common.is_package_path)(required_path) && (0, _common.is_global_path)(module.filename)) { var _absolute_path = _requireHacker2.default.resolve(required_path, module); var _result5 = { source: _requireHacker2.default.to_javascript_module_source(safe_require(_absolute_path, log)), path: _absolute_path }; return _result5; } } // if it's still here then it means it's either a fail(); } }); log.debug('compiling assets'); // timer start var began_at = new Date().getTime(); // evaluate parsed assets source code var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = (0, _getIterator3.default)((0, _keys2.default)(parsed_assets)), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var asset_path = _step5.value; var compile = true; // If this asset should be compiled at runtime then skip its compilation. // (runtime compilation can be used for Webpack loaders returning // javascript functions or React component classes — stuff like that, // because it's not serializable to `webpack-assets.json`) var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = (0, _getIterator3.default)((0, _keys2.default)(options.assets)), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var _asset_type = _step6.value; if (options.regular_expressions[_asset_type].test(asset_path)) { if (options.assets[_asset_type].runtime) { compile = false; } } } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } if (!compile) { output.assets[asset_path] = parsed_assets[asset_path]; continue; } // set asset value log.debug('compiling asset "' + asset_path + '"'); output.assets[asset_path] = safe_require(_path2.default.resolve(options.project_path, asset_path), log); // inside that require() call above // all the assets are resolved relative to this `module`, // which is irrelevant because they are all absolute filesystem paths. // // if in some of those assets a nested require() call is present // then it will be resolved relative to that asset folder. } // unmount the previously installed require() hook } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } require_hook.unmount(); // timer stop log.debug(' time taken: ' + (new Date().getTime() - began_at) + ' ms'); } function safe_require(path, log) { try { return require(path); } catch (error) { log.error(error); return undefined; } } function extract_path(from) { var question_mark_index = from.indexOf('?'); if (question_mark_index === -1) { return from; } return from.slice(0, question_mark_index); } //# sourceMappingURL=write assets.js.map