• bradfordlemley's avatar
    Add support for yarn and lerna monorepos. (#3741) · b43ad04b
    bradfordlemley authored
    * Support for multiple source paths via package.json srcPaths entry.
    
    Initial support for yarn workspace.
    
    Support lerna workspace, fix for when to use template files.
    
    Remove support for specifying srcPaths in package.json.
    
    Re-enable transpilation caching.
    
    * Clean up, use file matching (similar to original) in webpack configs instead of matching function.
    
    * Remove package lock files.
    
    * Fix for eject.
    Note: monorepos won't work after eject.
    Can be fixed easily with JEST 22.0.?+ which has file pattern matches against realpaths.
    
    * Filter tests to run only tests in monorepo components included by the app.
    (Not sure this is desireable, might be cool to be able to easily run all tests in monorepo from one app.)
    
    * Fix conditions for when to use template.
    
    * Fix eject.
    
    * Remove code that is not needed w/ Jest 22.
    
    * Include all cra-comp tests in monorepo instead of trying to include only tests that are dependencies of app.
    (tests can be easily filtered via jest cli if desired, e.g. 'npm test -- myapp comp1')
    
    * Pin find-pkg version.
    
    * Hopefully fix jest test file matching on windows by removing first slash.
    
    * E2E tests for monorepo.
    
    * Run monorepo tests in CI.
    
    * Fix and test post-eject build.
    
    * Fix e2e test.
    
    * Fix test suite names in appveyor.
    
    * Include individual package dirs as srcPaths instead of top-level monorepo root.
    Fixes build/start after eject.
    
    * Fix running tests after eject.
    
    * Clean up test workspace, add some verifcations.
    
    * Cleanup.
    
    * Try to fix hang when running test on appveyor.
    
    * Don't write babel or lint config to package.json when ejecting.
    
    * Incorporate review comments.
    * Simply monorepo pkg finder
    * Only include monorepo pkgs if app itself is included in monorepo
    * Check for specific tests in e2e
    
    * Fixes for windows.
    
    * Fix for kitchensink mocha tests compiling.
    
    * Add lerna monorepo test.
    
    * Fix lerna bootstrap on windows.
    
    * Incorporate more review comments:
    * remove support for lerna w/o yarn workspace
    * add react and react-dom as devDeps to comp1 and comp2
    
    * Add monorepo info to user guide.
    b43ad04b
webpackHotDevClient.js 8.93 KiB
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';
// This alternative WebpackDevServer combines the functionality of:
// https://github.com/webpack/webpack-dev-server/blob/webpack-1/client/index.js
// https://github.com/webpack/webpack/blob/webpack-1/hot/dev-server.js
// It only supports their simplest configuration (hot updates on same server).
// It makes some opinionated choices on top, like adding a syntax error overlay
// that looks similar to our console output. The error overlay is inspired by:
// https://github.com/glenjamin/webpack-hot-middleware
var ansiHTML = require('ansi-html');
var SockJS = require('sockjs-client');
var stripAnsi = require('strip-ansi');
var url = require('url');
var formatWebpackMessages = require('./formatWebpackMessages');
var Entities = require('html-entities').AllHtmlEntities;
var entities = new Entities();
// Color scheme inspired by https://github.com/glenjamin/webpack-hot-middleware
var colors = {
  reset: ['transparent', 'transparent'],
  black: '181818',
  red: 'E36049',
  green: 'B3CB74',
  yellow: 'FFD080',
  blue: '7CAFC2',
  magenta: '7FACCA',
  cyan: 'C3C2EF',
  lightgrey: 'EBE7E3',
  darkgrey: '6D7891'
ansiHTML.setColors(colors);
function createOverlayIframe(onIframeLoad) {
  var iframe = document.createElement('iframe');
  iframe.id = 'react-dev-utils-webpack-hot-dev-client-overlay';
  iframe.src = 'about:blank';
  iframe.style.position = 'fixed';
  iframe.style.left = 0;
  iframe.style.top = 0;
  iframe.style.right = 0;
  iframe.style.bottom = 0;
  iframe.style.width = '100vw';
  iframe.style.height = '100vh';
  iframe.style.border = 'none';
  iframe.style.zIndex = 9999999999;
  iframe.onload = onIframeLoad;
  return iframe;
function addOverlayDivTo(iframe) {
  var div =  iframe.contentDocument.createElement('div');
  div.id = 'react-dev-utils-webpack-hot-dev-client-overlay-div';
  div.style.position = 'fixed';
  div.style.boxSizing = 'border-box';
  div.style.left = 0;
  div.style.top = 0;
  div.style.right = 0;
  div.style.bottom = 0;
  div.style.width = '100vw';
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
div.style.height = '100vh'; div.style.backgroundColor = 'black'; div.style.color = '#E8E8E8'; div.style.fontFamily = 'Menlo, Consolas, monospace'; div.style.fontSize = 'large'; div.style.padding = '2rem'; div.style.lineHeight = '1.2'; div.style.whiteSpace = 'pre-wrap'; div.style.overflow = 'auto'; iframe.contentDocument.body.appendChild(div); return div; } var overlayIframe = null; var overlayDiv = null; var lastOnOverlayDivReady = null; function ensureOverlayDivExists(onOverlayDivReady) { if (overlayDiv) { // Everything is ready, call the callback right away. onOverlayDivReady(overlayDiv); return; } // Creating an iframe may be asynchronous so we'll schedule the callback. // In case of multiple calls, last callback wins. lastOnOverlayDivReady = onOverlayDivReady; if (overlayIframe) { // We're already creating it. return; } // Create iframe and, when it is ready, a div inside it. overlayIframe = createOverlayIframe(function onIframeLoad() { overlayDiv = addOverlayDivTo(overlayIframe); // Now we can talk! lastOnOverlayDivReady(overlayDiv); }); // Zalgo alert: onIframeLoad() will be called either synchronously // or asynchronously depending on the browser. // We delay adding it so `overlayIframe` is set when `onIframeLoad` fires. document.body.appendChild(overlayIframe); } function showErrorOverlay(message) { ensureOverlayDivExists(function onOverlayDivReady(overlayDiv) { // Make it look similar to our terminal. overlayDiv.innerHTML = '<span style="color: #' + colors.red + '">Failed to compile.</span><br><br>' + ansiHTML(entities.encode(message)); }); } function destroyErrorOverlay() { if (!overlayDiv) { // It is not there in the first place. return; } // Clean up and reset internal state. document.body.removeChild(overlayIframe); overlayDiv = null; overlayIframe = null; lastOnOverlayDivReady = null; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
// Connect to WebpackDevServer via a socket. var connection = new SockJS(url.format({ protocol: window.location.protocol, hostname: window.location.hostname, port: window.location.port, // Hardcoded in WebpackDevServer pathname: '/sockjs-node' })); // Unlike WebpackDevServer client, we won't try to reconnect // to avoid spamming the console. Disconnect usually happens // when developer stops the server. connection.onclose = function() { console.info( 'The development server has disconnected.\nRefresh the page if necessary.' ); }; // Remember some state related to hot module replacement. var isFirstCompilation = true; var mostRecentCompilationHash = null; var hasCompileErrors = false; function clearOutdatedErrors() { // Clean up outdated compile errors, if any. if (hasCompileErrors && typeof console.clear === 'function') { console.clear(); } } // Successful compilation. function handleSuccess() { clearOutdatedErrors(); destroyErrorOverlay(); var isHotUpdate = !isFirstCompilation; isFirstCompilation = false; hasCompileErrors = false; // Attempt to apply hot updates or reload. if (isHotUpdate) { tryApplyUpdates(); } } // Compilation with warnings (e.g. ESLint). function handleWarnings(warnings) { clearOutdatedErrors(); destroyErrorOverlay(); var isHotUpdate = !isFirstCompilation; isFirstCompilation = false; hasCompileErrors = false; function printWarnings() { // Print warnings to the console. for (var i = 0; i < warnings.length; i++) { console.warn(stripAnsi(warnings[i])); } } // Attempt to apply hot updates or reload. if (isHotUpdate) { tryApplyUpdates(function onSuccessfulHotUpdate() { // Only print warnings if we aren't refreshing the page. // Otherwise they'll disappear right away anyway. printWarnings(); }); } else { // Print initial warnings immediately.
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
printWarnings(); } } // Compilation with errors (e.g. syntax error or missing modules). function handleErrors(errors) { clearOutdatedErrors(); isFirstCompilation = false; hasCompileErrors = true; // "Massage" webpack messages. var formatted = formatWebpackMessages({ errors: errors, warnings: [] }); // Only show the first error. showErrorOverlay(formatted.errors[0]); // Also log them to the console. for (var i = 0; i < formatted.errors.length; i++) { console.error(stripAnsi(formatted.errors[i])); } // Do not attempt to reload now. // We will reload on next success instead. } // There is a newer version of the code available. function handleAvailableHash(hash) { // Update last known compilation hash. mostRecentCompilationHash = hash; } // Handle messages from the server. connection.onmessage = function(e) { var message = JSON.parse(e.data); switch (message.type) { case 'hash': handleAvailableHash(message.data); break; case 'still-ok': case 'ok': handleSuccess(); break; case 'content-changed': // Triggered when a file from `contentBase` changed. window.location.reload(); break; case 'warnings': handleWarnings(message.data); break; case 'errors': handleErrors(message.data); break; default: // Do nothing. } } // Is there a newer version of this code available? function isUpdateAvailable() { /* globals __webpack_hash__ */ // __webpack_hash__ is the hash of the current compilation. // It's a global variable injected by Webpack. return mostRecentCompilationHash !== __webpack_hash__; } // Webpack disallows updates in other states.
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
function canApplyUpdates() { return module.hot.status() === 'idle'; } // Attempt to update code on the fly, fall back to a hard reload. function tryApplyUpdates(onHotUpdateSuccess) { if (!module.hot) { // HotModuleReplacementPlugin is not in Webpack configuration. window.location.reload(); return; } if (!isUpdateAvailable() || !canApplyUpdates()) { return; } function handleApplyUpdates(err, updatedModules) { if (err || !updatedModules) { window.location.reload(); return; } if (typeof onHotUpdateSuccess === 'function') { // Maybe we want to do something. onHotUpdateSuccess(); } if (isUpdateAvailable()) { // While we were updating, there was a new update! Do it again. tryApplyUpdates(); } } // https://webpack.github.io/docs/hot-module-replacement.html#check var result = module.hot.check(/* autoApply */true, handleApplyUpdates); // // Webpack 2 returns a Promise instead of invoking a callback if (result && result.then) { result.then( function(updatedModules) { handleApplyUpdates(null, updatedModules); }, function(err) { handleApplyUpdates(err, null); } ); } }