/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * strict-local * @format */ "use strict"; function _slicedToArray(arr, i) { return ( _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest() ); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for ( var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true ) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === "function") { ownKeys = ownKeys.concat( Object.getOwnPropertySymbols(source).filter(function(sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; }) ); } ownKeys.forEach(function(key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } const nullthrows = require("nullthrows"); function getInternalOptions(_ref) { let transform = _ref.transform, resolve = _ref.resolve, onProgress = _ref.onProgress; let numProcessed = 0; let total = 0; return { transform, resolve, onDependencyAdd: () => onProgress && onProgress(numProcessed, ++total), onDependencyAdded: () => onProgress && onProgress(++numProcessed, total) }; } /** * Dependency Traversal logic for the Delta Bundler. This method calculates * the modules that should be included in the bundle by traversing the * dependency graph. * Instead of traversing the whole graph each time, it just calculates the * difference between runs by only traversing the added/removed dependencies. * To do so, it uses the passed passed graph dependencies and it mutates it. * The paths parameter contains the absolute paths of the root files that the * method should traverse. Normally, these paths should be the modified files * since the last traversal. */ function traverseDependencies(_x, _x2, _x3) { return _traverseDependencies.apply(this, arguments); } function _traverseDependencies() { _traverseDependencies = _asyncToGenerator(function*(paths, graph, options) { const delta = { added: new Set(), modified: new Set(), deleted: new Set(), inverseDependencies: new Map() }; const internalOptions = getInternalOptions(options); for (const path of paths) { // Only process the path if it's part of the dependency graph. It's possible // that this method receives a path that is no longer part of it (e.g if a // module gets removed from the dependency graph and just afterwards it gets // modified), and we have to ignore these cases. if (graph.dependencies.get(path)) { delta.modified.add(path); yield traverseDependenciesForSingleFile( path, graph, delta, internalOptions ); } } const added = new Map(); const modified = new Map(); const deleted = new Set(); for (const path of delta.deleted) { // If a dependency has been marked both as added and deleted, it means that // this is a renamed file (or that dependency has been removed from one path // but added back in a different path). In this case the addition and // deletion "get cancelled". if (!delta.added.has(path)) { deleted.add(path); } delta.modified.delete(path); delta.added.delete(path); } for (const path of delta.added) { added.set(path, nullthrows(graph.dependencies.get(path))); } for (const path of delta.modified) { // Similarly to the above, a file can be marked as both added and modified // when its path and dependencies have changed. In this case, we only // consider the addition. if (!delta.added.has(path)) { modified.set(path, nullthrows(graph.dependencies.get(path))); } } return { added, modified, deleted }; }); return _traverseDependencies.apply(this, arguments); } function initialTraverseDependencies(_x4, _x5) { return _initialTraverseDependencies.apply(this, arguments); } function _initialTraverseDependencies() { _initialTraverseDependencies = _asyncToGenerator(function*(graph, options) { const delta = { added: new Set(), modified: new Set(), deleted: new Set(), inverseDependencies: new Map() }; const internalOptions = getInternalOptions(options); yield Promise.all( graph.entryPoints.map(path => traverseDependenciesForSingleFile(path, graph, delta, internalOptions) ) ); reorderGraph(graph); return { added: graph.dependencies, modified: new Map(), deleted: new Set() }; }); return _initialTraverseDependencies.apply(this, arguments); } function traverseDependenciesForSingleFile(_x6, _x7, _x8, _x9) { return _traverseDependenciesForSingleFile.apply(this, arguments); } function _traverseDependenciesForSingleFile() { _traverseDependenciesForSingleFile = _asyncToGenerator(function*( path, graph, delta, options ) { options.onDependencyAdd(); yield processModule(path, graph, delta, options); options.onDependencyAdded(); }); return _traverseDependenciesForSingleFile.apply(this, arguments); } function processModule(_x10, _x11, _x12, _x13) { return _processModule.apply(this, arguments); } function _processModule() { _processModule = _asyncToGenerator(function*(path, graph, delta, options) { // Transform the file via the given option. const result = yield options.transform(path); // Get the absolute path of all sub-dependencies (some of them could have been // moved but maintain the same relative path). const currentDependencies = resolveDependencies( path, result.dependencies, options ); const previousModule = graph.dependencies.get(path) || { inverseDependencies: delta.inverseDependencies.get(path) || new Set(), path }; const previousDependencies = previousModule.dependencies || new Map(); // Update the module information. const module = _objectSpread({}, previousModule, { dependencies: new Map(), getSource: result.getSource, output: result.output }); graph.dependencies.set(module.path, module); for (const _ref2 of currentDependencies) { var _ref3 = _slicedToArray(_ref2, 2); const relativePath = _ref3[0]; const dependency = _ref3[1]; module.dependencies.set(relativePath, dependency); } for (const _ref4 of previousDependencies) { var _ref5 = _slicedToArray(_ref4, 2); const relativePath = _ref5[0]; const dependency = _ref5[1]; if (!currentDependencies.has(relativePath)) { removeDependency(module, dependency.absolutePath, graph, delta); } } // Check all the module dependencies and start traversing the tree from each // added and removed dependency, to get all the modules that have to be added // and removed from the dependency graph. const promises = []; for (const _ref6 of currentDependencies) { var _ref7 = _slicedToArray(_ref6, 2); const relativePath = _ref7[0]; const dependency = _ref7[1]; if (!previousDependencies.has(relativePath)) { promises.push( addDependency(module, dependency.absolutePath, graph, delta, options) ); } } yield Promise.all(promises); return module; }); return _processModule.apply(this, arguments); } function addDependency(_x14, _x15, _x16, _x17, _x18) { return _addDependency.apply(this, arguments); } function _addDependency() { _addDependency = _asyncToGenerator(function*( parentModule, path, graph, delta, options ) { // The new dependency was already in the graph, we don't need to do anything. const existingModule = graph.dependencies.get(path); if (existingModule) { existingModule.inverseDependencies.add(parentModule.path); return; } // This module is being transformed at the moment in parallel, so we should // only mark its parent as an inverse dependency. const inverse = delta.inverseDependencies.get(path); if (inverse) { inverse.add(parentModule.path); return; } delta.added.add(path); delta.inverseDependencies.set(path, new Set([parentModule.path])); options.onDependencyAdd(); const module = yield processModule(path, graph, delta, options); graph.dependencies.set(module.path, module); module.inverseDependencies.add(parentModule.path); options.onDependencyAdded(); }); return _addDependency.apply(this, arguments); } function removeDependency(parentModule, absolutePath, graph, delta) { const module = graph.dependencies.get(absolutePath); if (!module) { return; } module.inverseDependencies.delete(parentModule.path); // This module is still used by another modules, so we cannot remove it from // the bundle. if (module.inverseDependencies.size) { return; } delta.deleted.add(module.path); // Now we need to iterate through the module dependencies in order to // clean up everything (we cannot read the module because it may have // been deleted). for (const dependency of module.dependencies.values()) { removeDependency(module, dependency.absolutePath, graph, delta); } // This module is not used anywhere else!! we can clear it from the bundle graph.dependencies.delete(module.path); } function resolveDependencies(parentPath, dependencies, options) { return new Map( dependencies.map(result => { const relativePath = result.name; const dependency = { absolutePath: options.resolve(parentPath, result.name), data: result }; return [relativePath, dependency]; }) ); } /** * Re-traverse the dependency graph in DFS order to reorder the modules and * guarantee the same order between runs. This method mutates the passed graph. */ function reorderGraph(graph) { const orderedDependencies = new Map(); graph.entryPoints.forEach(entryPoint => { const mainModule = graph.dependencies.get(entryPoint); if (!mainModule) { throw new ReferenceError("Module not registered in graph: " + entryPoint); } reorderDependencies(graph, mainModule, orderedDependencies); }); graph.dependencies = orderedDependencies; } function reorderDependencies(graph, module, orderedDependencies) { if (module.path) { if (orderedDependencies.has(module.path)) { return; } orderedDependencies.set(module.path, module); } module.dependencies.forEach(dependency => { const path = dependency.absolutePath; const childModule = graph.dependencies.get(path); if (!childModule) { throw new ReferenceError("Module not registered in graph: " + path); } reorderDependencies(graph, childModule, orderedDependencies); }); } module.exports = { initialTraverseDependencies, traverseDependencies, reorderGraph };