فهرست منبع

last fix in #6998: the `--watch` build is fixed

This fixes the problem with the `--watch` build (which was caused by a
depopulation of "linkedPackages" in `dev_mode/package.json`). It still
needs a bit of cleanup work
telamonian 5 سال پیش
والد
کامیت
4d4a257e33
4فایلهای تغییر یافته به همراه412 افزوده شده و 138 حذف شده
  1. 15 0
      buildutils/src/ensure-repo.ts
  2. 118 2
      dev_mode/package.json
  3. 136 57
      dev_mode/webpack.config.js
  4. 143 79
      jupyterlab/staging/webpack.config.js

+ 15 - 0
buildutils/src/ensure-repo.ts

@@ -144,6 +144,7 @@ function ensureJupyterlab(): string[] {
   corePackage.jupyterlab.extensions = {};
   corePackage.jupyterlab.mimeExtensions = {};
   corePackage.jupyterlab.linkedPackages = {};
+  corePackage.jupyterlab.watchedPackages = {};
   corePackage.dependencies = {};
 
   let singletonPackages = corePackage.jupyterlab.singletonPackages;
@@ -211,6 +212,20 @@ function ensureJupyterlab(): string[] {
     });
   });
 
+  utils.getLernaPaths().forEach(pkgPath => {
+    let dataPath = path.join(pkgPath, 'package.json');
+    let data: any;
+    try {
+      data = utils.readJSONFile(dataPath);
+    } catch (e) {
+      return;
+    }
+
+    // watch all src, build, and test files in the Jupyterlab project
+    let relativePath = `../${path.relative(basePath, pkgPath)}`;
+    corePackage.jupyterlab.watchedPackages[data.name] = relativePath;
+  });
+
   // Write the package.json back to disk.
   if (utils.writePackageData(corePath, corePackage)) {
     return ['Updated dev mode'];

+ 118 - 2
dev_mode/package.json

@@ -116,6 +116,8 @@
     "node": ">=6.11.5"
   },
   "jupyterlab": {
+    "name": "JupyterLab",
+    "version": "1.0.0rc0",
     "extensions": {
       "@jupyterlab/application-extension": "",
       "@jupyterlab/apputils-extension": "",
@@ -157,7 +159,6 @@
       "@jupyterlab/vega4-extension": "",
       "@jupyterlab/vega5-extension": ""
     },
-    "name": "JupyterLab",
     "buildDir": "./static",
     "outputDir": ".",
     "singletonPackages": [
@@ -209,7 +210,6 @@
       "url-parse",
       "xterm"
     ],
-    "version": "1.0.0rc0",
     "linkedPackages": {
       "@jupyterlab/application": "../packages/application",
       "@jupyterlab/application-extension": "../packages/application-extension",
@@ -250,6 +250,122 @@
       "@jupyterlab/vdom-extension": "../packages/vdom-extension",
       "@jupyterlab/vega4-extension": "../packages/vega4-extension",
       "@jupyterlab/vega5-extension": "../packages/vega5-extension"
+    },
+    "watchedPackages": {
+      "@jupyterlab/application-top": "../dev_mode",
+      "@jupyterlab/example-app": "../examples/app",
+      "@jupyterlab/example-cell": "../examples/cell",
+      "@jupyterlab/example-console": "../examples/console",
+      "@jupyterlab/example-filebrowser": "../examples/filebrowser",
+      "@jupyterlab/example-notebook": "../examples/notebook",
+      "@jupyterlab/example-terminal": "../examples/terminal",
+      "@jupyterlab/application": "../packages/application",
+      "@jupyterlab/application-extension": "../packages/application-extension",
+      "@jupyterlab/apputils": "../packages/apputils",
+      "@jupyterlab/apputils-extension": "../packages/apputils-extension",
+      "@jupyterlab/attachments": "../packages/attachments",
+      "@jupyterlab/cells": "../packages/cells",
+      "@jupyterlab/codeeditor": "../packages/codeeditor",
+      "@jupyterlab/codemirror": "../packages/codemirror",
+      "@jupyterlab/codemirror-extension": "../packages/codemirror-extension",
+      "@jupyterlab/completer": "../packages/completer",
+      "@jupyterlab/completer-extension": "../packages/completer-extension",
+      "@jupyterlab/console": "../packages/console",
+      "@jupyterlab/console-extension": "../packages/console-extension",
+      "@jupyterlab/coreutils": "../packages/coreutils",
+      "@jupyterlab/csvviewer": "../packages/csvviewer",
+      "@jupyterlab/csvviewer-extension": "../packages/csvviewer-extension",
+      "@jupyterlab/docmanager": "../packages/docmanager",
+      "@jupyterlab/docmanager-extension": "../packages/docmanager-extension",
+      "@jupyterlab/docregistry": "../packages/docregistry",
+      "@jupyterlab/documentsearch": "../packages/documentsearch",
+      "@jupyterlab/documentsearch-extension": "../packages/documentsearch-extension",
+      "@jupyterlab/extensionmanager": "../packages/extensionmanager",
+      "@jupyterlab/extensionmanager-extension": "../packages/extensionmanager-extension",
+      "@jupyterlab/filebrowser": "../packages/filebrowser",
+      "@jupyterlab/filebrowser-extension": "../packages/filebrowser-extension",
+      "@jupyterlab/fileeditor": "../packages/fileeditor",
+      "@jupyterlab/fileeditor-extension": "../packages/fileeditor-extension",
+      "@jupyterlab/help-extension": "../packages/help-extension",
+      "@jupyterlab/htmlviewer": "../packages/htmlviewer",
+      "@jupyterlab/htmlviewer-extension": "../packages/htmlviewer-extension",
+      "@jupyterlab/hub-extension": "../packages/hub-extension",
+      "@jupyterlab/imageviewer": "../packages/imageviewer",
+      "@jupyterlab/imageviewer-extension": "../packages/imageviewer-extension",
+      "@jupyterlab/inspector": "../packages/inspector",
+      "@jupyterlab/inspector-extension": "../packages/inspector-extension",
+      "@jupyterlab/javascript-extension": "../packages/javascript-extension",
+      "@jupyterlab/json-extension": "../packages/json-extension",
+      "@jupyterlab/launcher": "../packages/launcher",
+      "@jupyterlab/launcher-extension": "../packages/launcher-extension",
+      "@jupyterlab/mainmenu": "../packages/mainmenu",
+      "@jupyterlab/mainmenu-extension": "../packages/mainmenu-extension",
+      "@jupyterlab/markdownviewer": "../packages/markdownviewer",
+      "@jupyterlab/markdownviewer-extension": "../packages/markdownviewer-extension",
+      "@jupyterlab/mathjax2": "../packages/mathjax2",
+      "@jupyterlab/mathjax2-extension": "../packages/mathjax2-extension",
+      "@jupyterlab/metapackage": "../packages/metapackage",
+      "@jupyterlab/nbconvert-css": "../packages/nbconvert-css",
+      "@jupyterlab/notebook": "../packages/notebook",
+      "@jupyterlab/notebook-extension": "../packages/notebook-extension",
+      "@jupyterlab/observables": "../packages/observables",
+      "@jupyterlab/outputarea": "../packages/outputarea",
+      "@jupyterlab/pdf-extension": "../packages/pdf-extension",
+      "@jupyterlab/rendermime": "../packages/rendermime",
+      "@jupyterlab/rendermime-extension": "../packages/rendermime-extension",
+      "@jupyterlab/rendermime-interfaces": "../packages/rendermime-interfaces",
+      "@jupyterlab/running": "../packages/running",
+      "@jupyterlab/running-extension": "../packages/running-extension",
+      "@jupyterlab/services": "../packages/services",
+      "@jupyterlab/settingeditor": "../packages/settingeditor",
+      "@jupyterlab/settingeditor-extension": "../packages/settingeditor-extension",
+      "@jupyterlab/shortcuts-extension": "../packages/shortcuts-extension",
+      "@jupyterlab/statusbar": "../packages/statusbar",
+      "@jupyterlab/statusbar-extension": "../packages/statusbar-extension",
+      "@jupyterlab/tabmanager-extension": "../packages/tabmanager-extension",
+      "@jupyterlab/terminal": "../packages/terminal",
+      "@jupyterlab/terminal-extension": "../packages/terminal-extension",
+      "@jupyterlab/theme-dark-extension": "../packages/theme-dark-extension",
+      "@jupyterlab/theme-light-extension": "../packages/theme-light-extension",
+      "@jupyterlab/tooltip": "../packages/tooltip",
+      "@jupyterlab/tooltip-extension": "../packages/tooltip-extension",
+      "@jupyterlab/ui-components": "../packages/ui-components",
+      "@jupyterlab/vdom": "../packages/vdom",
+      "@jupyterlab/vdom-extension": "../packages/vdom-extension",
+      "@jupyterlab/vega4-extension": "../packages/vega4-extension",
+      "@jupyterlab/vega5-extension": "../packages/vega5-extension",
+      "node-example": "../packages/services/examples/node",
+      "@jupyterlab/example-services-browser": "../packages/services/examples/browser",
+      "@jupyterlab/example-services-outputarea": "../packages/services/examples/typescript-browser-with-output",
+      "@jupyterlab/buildutils": "../buildutils",
+      "@jupyterlab/template": "../buildutils/template",
+      "@jupyterlab/template-for-tests": "../buildutils/test-template",
+      "@jupyterlab/test-root": "../tests",
+      "@jupyterlab/test-application": "../tests/test-application",
+      "@jupyterlab/test-apputils": "../tests/test-apputils",
+      "@jupyterlab/test-cells": "../tests/test-cells",
+      "@jupyterlab/test-codeeditor": "../tests/test-codeeditor",
+      "@jupyterlab/test-codemirror": "../tests/test-codemirror",
+      "@jupyterlab/test-completer": "../tests/test-completer",
+      "@jupyterlab/test-console": "../tests/test-console",
+      "@jupyterlab/test-coreutils": "../tests/test-coreutils",
+      "@jupyterlab/test-csvviewer": "../tests/test-csvviewer",
+      "@jupyterlab/test-docmanager": "../tests/test-docmanager",
+      "@jupyterlab/test-docregistry": "../tests/test-docregistry",
+      "@jupyterlab/test-filebrowser": "../tests/test-filebrowser",
+      "@jupyterlab/test-fileeditor": "../tests/test-fileeditor",
+      "@jupyterlab/test-imageviewer": "../tests/test-imageviewer",
+      "@jupyterlab/test-inspector": "../tests/test-inspector",
+      "@jupyterlab/test-mainmenu": "../tests/test-mainmenu",
+      "@jupyterlab/test-notebook": "../tests/test-notebook",
+      "@jupyterlab/test-observables": "../tests/test-observables",
+      "@jupyterlab/test-outputarea": "../tests/test-outputarea",
+      "@jupyterlab/test-rendermime": "../tests/test-rendermime",
+      "@jupyterlab/test-services": "../tests/test-services",
+      "@jupyterlab/test-statusbar": "../tests/test-statusbar",
+      "@jupyterlab/test-terminal": "../tests/test-terminal",
+      "@jupyterlab/testutils": "../testutils",
+      "@jupyterlab/mock-extension": "../jupyterlab/tests/mock_packages/extension"
     }
   }
 }

+ 136 - 57
dev_mode/webpack.config.js

@@ -3,7 +3,7 @@
 | Distributed under the terms of the Modified BSD License.
 |----------------------------------------------------------------------------*/
 
-var path = require('path');
+var plib = require('path');
 var fs = require('fs-extra');
 var Handlebars = require('handlebars');
 var HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -21,7 +21,7 @@ var mimeExtensions = jlab.mimeExtensions;
 var packageNames = Object.keys(mimeExtensions).concat(Object.keys(extensions));
 
 // Ensure a clear build directory.
-var buildDir = path.resolve(jlab.buildDir);
+var buildDir = plib.resolve(jlab.buildDir);
 if (fs.existsSync(buildDir)) {
   fs.removeSync(buildDir);
 }
@@ -42,21 +42,21 @@ var data = {
 };
 var result = template(data);
 
-fs.writeFileSync(path.join(buildDir, 'index.out.js'), result);
-fs.copySync('./package.json', path.join(buildDir, 'package.json'));
+fs.writeFileSync(plib.join(buildDir, 'index.out.js'), result);
+fs.copySync('./package.json', plib.join(buildDir, 'package.json'));
 fs.copySync(
-  path.join(jlab.outputDir, 'imports.css'),
-  path.join(buildDir, 'imports.css')
+  plib.join(jlab.outputDir, 'imports.css'),
+  plib.join(buildDir, 'imports.css')
 );
 
 // Set up variables for watch mode.
-var localLinked = {};
+var watched = {};
 var ignoreCache = Object.create(null);
-Object.keys(jlab.linkedPackages).forEach(function(name) {
-  var localPath = require.resolve(path.join(name, 'package.json'));
-  localLinked[name] = path.dirname(localPath);
+Object.keys(jlab.watchedPackages).forEach(function(name) {
+  if (name in watched) return;
+  var localPath = require.resolve(plib.join(name, 'package.json'));
+  watched[name] = plib.dirname(localPath);
 });
-var ignorePatterns = [/^\.\#/]; // eslint-disable-line
 
 /**
  * Sync a local path to a linked package path if they are files and differ.
@@ -66,7 +66,7 @@ function maybeSync(localPath, name, rest) {
   if (!stats.isFile(localPath)) {
     return;
   }
-  var source = fs.realpathSync(path.join(jlab.linkedPackages[name], rest));
+  var source = fs.realpathSync(plib.join(jlab.watchedPackages[name], rest));
   if (source === fs.realpathSync(localPath)) {
     return;
   }
@@ -109,7 +109,125 @@ JupyterFrontEndPlugin.prototype.apply = function(compiler) {
 
 JupyterFrontEndPlugin.prototype._first = true;
 
+const ignored = path => {
+  path = plib.resolve(path);
+  if (path in ignoreCache) {
+    return ignoreCache[path];
+  }
+
+  // Limit the watched files to those in our local linked package dirs.
+  var ignore = true;
+  Object.keys(watched).some(function(name) {
+    console.error(name);
+    // Bail if already found.
+    var rootPath = watched[name];
+    var contained = path.indexOf(rootPath + plib.sep) !== -1;
+    if (path !== rootPath && !contained) {
+      return false;
+    }
+    var rest = path.slice(rootPath.length);
+    if (rest.indexOf('node_modules') === -1) {
+      ignore = false;
+      maybeSync(path, name, rest);
+    }
+    return true;
+  });
+  ignoreCache[path] = ignore;
+  return ignore;
+};
+
+class JupyterIgnorePlugin extends webpack.IgnorePlugin {
+  constructor() {
+    super({});
+  }
+
+  checkIgnore(result) {
+    if (!result) return result;
+    return ignored(result.resource) ? result : null;
+  }
+}
+
+class JupyterIgnoringWatchFileSystem {
+  constructor(wfs) {
+    this.wfs = wfs;
+  }
+
+  watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
+    const notIgnored = path => !ignored(path);
+
+    const ignoredFiles = files.filter(ignored);
+    const ignoredDirs = dirs.filter(ignored);
+
+    const watcher = this.wfs.watch(
+      files.filter(notIgnored),
+      dirs.filter(notIgnored),
+      missing,
+      startTime,
+      options,
+      (
+        err,
+        filesModified,
+        dirsModified,
+        missingModified,
+        fileTimestamps,
+        dirTimestamps,
+        removedFiles
+      ) => {
+        if (err) return callback(err);
+        for (const path of ignoredFiles) {
+          fileTimestamps.set(path, 1);
+        }
+
+        for (const path of ignoredDirs) {
+          dirTimestamps.set(path, 1);
+        }
+
+        callback(
+          err,
+          filesModified,
+          dirsModified,
+          missingModified,
+          fileTimestamps,
+          dirTimestamps,
+          removedFiles
+        );
+      },
+      callbackUndelayed
+    );
+
+    return {
+      close: () => watcher.close(),
+      pause: () => watcher.pause(),
+      getContextTimestamps: () => {
+        const dirTimestamps = watcher.getContextTimestamps();
+        for (const path of ignoredDirs) {
+          dirTimestamps.set(path, 1);
+        }
+        return dirTimestamps;
+      },
+      getFileTimestamps: () => {
+        const fileTimestamps = watcher.getFileTimestamps();
+        for (const path of ignoredFiles) {
+          fileTimestamps.set(path, 1);
+        }
+        return fileTimestamps;
+      }
+    };
+  }
+}
+
+class JupyterWatchIgnorePlugin {
+  apply(compiler) {
+    compiler.hooks.afterEnvironment.tap('WatchIgnorePlugin', () => {
+      compiler.watchFileSystem = new JupyterIgnoringWatchFileSystem(
+        compiler.watchFileSystem
+      );
+    });
+  }
+}
+
 const plugins = [
+  new JupyterWatchIgnorePlugin(),
   new DuplicatePackageCheckerPlugin({
     verbose: true,
     exclude(instance) {
@@ -120,7 +238,7 @@ const plugins = [
     }
   }),
   new HtmlWebpackPlugin({
-    template: path.join('templates', 'template.html'),
+    template: plib.join('templates', 'template.html'),
     title: jlab.name || 'JupyterLab'
   }),
   new webpack.HashedModuleIdsPlugin(),
@@ -135,10 +253,10 @@ module.exports = [
   {
     mode: 'development',
     entry: {
-      main: ['whatwg-fetch', path.resolve(buildDir, 'index.out.js')]
+      main: ['whatwg-fetch', plib.resolve(buildDir, 'index.out.js')]
     },
     output: {
-      path: path.resolve(buildDir),
+      path: plib.resolve(buildDir),
       publicPath: '{{page_config.fullStaticUrl}}/',
       filename: '[name].[chunkhash].js'
     },
@@ -185,53 +303,14 @@ module.exports = [
       ]
     },
     watchOptions: {
-      ignored: function(localPath) {
-        localPath = path.resolve(localPath);
-        if (localPath in ignoreCache) {
-          return ignoreCache[localPath];
-        }
-
-        // Ignore files with certain patterns
-        var baseName = localPath.replace(/^.*[\\\/]/, ''); // eslint-disable-line
-        if (
-          ignorePatterns.some(function(rexp) {
-            console.error(`${localPath}: ${baseName.match(rexp)}`);
-            return baseName.match(rexp);
-          })
-        ) {
-          console.error(`${localPath}: true`);
-          return true;
-        }
-
-        // Limit the watched files to those in our local linked package dirs.
-        var ignore = true;
-        Object.keys(localLinked).some(function(name) {
-          // Bail if already found.
-          var rootPath = localLinked[name];
-          var contained = localPath.indexOf(rootPath + path.sep) !== -1;
-          if (localPath !== rootPath && !contained) {
-            console.error(`${localPath}: false`);
-            return false;
-          }
-          var rest = localPath.slice(rootPath.length);
-          if (rest.indexOf('node_modules') === -1) {
-            ignore = false;
-            maybeSync(localPath, name, rest);
-          }
-          console.error(`${localPath}: true`);
-          return true;
-        });
-        ignoreCache[localPath] = ignore;
-        console.error(`${localPath}: ${ignore}`);
-        return ignore;
-      },
-      poll: 100
+      ignored: /node_modules/,
+      poll: 333
     },
     node: {
       fs: 'empty'
     },
     bail: true,
-    devtool: 'source-map',
+    devtool: 'inline-source-map',
     externals: ['node-fetch', 'ws'],
     plugins,
     stats: {

+ 143 - 79
jupyterlab/staging/webpack.config.js

@@ -3,7 +3,7 @@
 | Distributed under the terms of the Modified BSD License.
 |----------------------------------------------------------------------------*/
 
-var path = require('path');
+var plib = require('path');
 var fs = require('fs-extra');
 var Handlebars = require('handlebars');
 var HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -21,7 +21,7 @@ var mimeExtensions = jlab.mimeExtensions;
 var packageNames = Object.keys(mimeExtensions).concat(Object.keys(extensions));
 
 // Ensure a clear build directory.
-var buildDir = path.resolve(jlab.buildDir);
+var buildDir = plib.resolve(jlab.buildDir);
 if (fs.existsSync(buildDir)) {
   fs.removeSync(buildDir);
 }
@@ -42,21 +42,21 @@ var data = {
 };
 var result = template(data);
 
-fs.writeFileSync(path.join(buildDir, 'index.out.js'), result);
-fs.copySync('./package.json', path.join(buildDir, 'package.json'));
+fs.writeFileSync(plib.join(buildDir, 'index.out.js'), result);
+fs.copySync('./package.json', plib.join(buildDir, 'package.json'));
 fs.copySync(
-  path.join(jlab.outputDir, 'imports.css'),
-  path.join(buildDir, 'imports.css')
+  plib.join(jlab.outputDir, 'imports.css'),
+  plib.join(buildDir, 'imports.css')
 );
 
 // Set up variables for watch mode.
-var localLinked = {};
+var watched = {};
 var ignoreCache = Object.create(null);
-Object.keys(jlab.linkedPackages).forEach(function(name) {
-  var localPath = require.resolve(path.join(name, 'package.json'));
-  localLinked[name] = path.dirname(localPath);
+Object.keys(jlab.watchedPackages).forEach(function(name) {
+  if (name in watched) return;
+  var localPath = require.resolve(plib.join(name, 'package.json'));
+  watched[name] = plib.dirname(localPath);
 });
-var ignorePatterns = [/^\.\#/]; // eslint-disable-line
 
 /**
  * Sync a local path to a linked package path if they are files and differ.
@@ -66,7 +66,7 @@ function maybeSync(localPath, name, rest) {
   if (!stats.isFile(localPath)) {
     return;
   }
-  var source = fs.realpathSync(path.join(jlab.linkedPackages[name], rest));
+  var source = fs.realpathSync(plib.join(jlab.watchedPackages[name], rest));
   if (source === fs.realpathSync(localPath)) {
     return;
   }
@@ -109,7 +109,125 @@ JupyterFrontEndPlugin.prototype.apply = function(compiler) {
 
 JupyterFrontEndPlugin.prototype._first = true;
 
+const ignored = path => {
+  path = plib.resolve(path);
+  if (path in ignoreCache) {
+    return ignoreCache[path];
+  }
+
+  // Limit the watched files to those in our local linked package dirs.
+  var ignore = true;
+  Object.keys(watched).some(function(name) {
+    console.error(name);
+    // Bail if already found.
+    var rootPath = watched[name];
+    var contained = path.indexOf(rootPath + plib.sep) !== -1;
+    if (path !== rootPath && !contained) {
+      return false;
+    }
+    var rest = path.slice(rootPath.length);
+    if (rest.indexOf('node_modules') === -1) {
+      ignore = false;
+      maybeSync(path, name, rest);
+    }
+    return true;
+  });
+  ignoreCache[path] = ignore;
+  return ignore;
+};
+
+class JupyterIgnorePlugin extends webpack.IgnorePlugin {
+  constructor() {
+    super({});
+  }
+
+  checkIgnore(result) {
+    if (!result) return result;
+    return ignored(result.resource) ? result : null;
+  }
+}
+
+class JupyterIgnoringWatchFileSystem {
+  constructor(wfs) {
+    this.wfs = wfs;
+  }
+
+  watch(files, dirs, missing, startTime, options, callback, callbackUndelayed) {
+    const notIgnored = path => !ignored(path);
+
+    const ignoredFiles = files.filter(ignored);
+    const ignoredDirs = dirs.filter(ignored);
+
+    const watcher = this.wfs.watch(
+      files.filter(notIgnored),
+      dirs.filter(notIgnored),
+      missing,
+      startTime,
+      options,
+      (
+        err,
+        filesModified,
+        dirsModified,
+        missingModified,
+        fileTimestamps,
+        dirTimestamps,
+        removedFiles
+      ) => {
+        if (err) return callback(err);
+        for (const path of ignoredFiles) {
+          fileTimestamps.set(path, 1);
+        }
+
+        for (const path of ignoredDirs) {
+          dirTimestamps.set(path, 1);
+        }
+
+        callback(
+          err,
+          filesModified,
+          dirsModified,
+          missingModified,
+          fileTimestamps,
+          dirTimestamps,
+          removedFiles
+        );
+      },
+      callbackUndelayed
+    );
+
+    return {
+      close: () => watcher.close(),
+      pause: () => watcher.pause(),
+      getContextTimestamps: () => {
+        const dirTimestamps = watcher.getContextTimestamps();
+        for (const path of ignoredDirs) {
+          dirTimestamps.set(path, 1);
+        }
+        return dirTimestamps;
+      },
+      getFileTimestamps: () => {
+        const fileTimestamps = watcher.getFileTimestamps();
+        for (const path of ignoredFiles) {
+          fileTimestamps.set(path, 1);
+        }
+        return fileTimestamps;
+      }
+    };
+  }
+}
+
+class JupyterWatchIgnorePlugin {
+  apply(compiler) {
+    compiler.hooks.afterEnvironment.tap('WatchIgnorePlugin', () => {
+      compiler.watchFileSystem = new JupyterIgnoringWatchFileSystem(
+        compiler.watchFileSystem
+      );
+    });
+  }
+}
+
 const plugins = [
+  new JupyterWatchIgnorePlugin(),
   new DuplicatePackageCheckerPlugin({
     verbose: true,
     exclude(instance) {
@@ -120,18 +238,10 @@ const plugins = [
     }
   }),
   new HtmlWebpackPlugin({
-    cache: false,
-    chunksSortMode: 'none',
-    template: path.join('templates', 'template.html'),
+    template: plib.join('templates', 'template.html'),
     title: jlab.name || 'JupyterLab'
   }),
   new webpack.HashedModuleIdsPlugin(),
-  new webpack.SourceMapDevToolPlugin({
-    filename: null,
-    exclude: [/node_modules/],
-    // apply this plugin only to .ts files - the rest is taken care of
-    test: /\.ts($|\?)/i
-  }),
   new JupyterFrontEndPlugin({})
 ];
 
@@ -143,10 +253,10 @@ module.exports = [
   {
     mode: 'development',
     entry: {
-      main: ['whatwg-fetch', path.resolve(buildDir, 'index.out.js')]
+      main: ['whatwg-fetch', plib.resolve(buildDir, 'index.out.js')]
     },
     output: {
-      path: path.resolve(buildDir),
+      path: plib.resolve(buildDir),
       publicPath: '{{page_config.fullStaticUrl}}/',
       filename: '[name].[chunkhash].js'
     },
@@ -160,27 +270,15 @@ module.exports = [
         { test: /\.css$/, use: ['style-loader', 'css-loader'] },
         { test: /\.md$/, use: 'raw-loader' },
         { test: /\.txt$/, use: 'raw-loader' },
-        // {
-        //   test: /\.js$/,
-        //   use: ['source-map-loader'],
-        //   enforce: 'pre',
-        //   // eslint-disable-next-line no-undef
-        //   exclude: /node_modules/
-        // },
-        { test: /\.(jpg|png|gif)$/, use: 'file-loader' },
-        // { test: /\.js.map$/, use: 'file-loader' },
         {
-          test: /\.tsx?$/,
-          exclude: /node_modules/,
-          use: [
-            {
-              loader: 'ts-loader',
-              options: {
-                experimentalWatchApi: true
-              }
-            }
-          ]
+          test: /\.js$/,
+          use: ['source-map-loader'],
+          enforce: 'pre',
+          // eslint-disable-next-line no-undef
+          exclude: /node_modules/
         },
+        { test: /\.(jpg|png|gif)$/, use: 'file-loader' },
+        { test: /\.js.map$/, use: 'file-loader' },
         {
           test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
           use: 'url-loader?limit=10000&mimetype=application/font-woff'
@@ -205,48 +303,14 @@ module.exports = [
       ]
     },
     watchOptions: {
-      ignored: function(localPath) {
-        localPath = path.resolve(localPath);
-        if (localPath in ignoreCache) {
-          return ignoreCache[localPath];
-        }
-
-        // Ignore files with certain patterns
-        var baseName = localPath.replace(/^.*[\\\/]/, ''); // eslint-disable-line
-        if (
-          ignorePatterns.some(function(rexp) {
-            return baseName.match(rexp);
-          })
-        ) {
-          return true;
-        }
-
-        // Limit the watched files to those in our local linked package dirs.
-        var ignore = true;
-        Object.keys(localLinked).some(function(name) {
-          // Bail if already found.
-          var rootPath = localLinked[name];
-          var contained = localPath.indexOf(rootPath + path.sep) !== -1;
-          if (localPath !== rootPath && !contained) {
-            return false;
-          }
-          var rest = localPath.slice(rootPath.length);
-          if (rest.indexOf('node_modules') === -1) {
-            ignore = false;
-            maybeSync(localPath, name, rest);
-          }
-          return true;
-        });
-        ignoreCache[localPath] = ignore;
-        return ignore;
-      },
-      poll: 100
+      ignored: /node_modules/,
+      poll: 333
     },
     node: {
       fs: 'empty'
     },
     bail: true,
-    devtool: 'source-map',
+    devtool: 'inline-source-map',
     externals: ['node-fetch', 'ws'],
     plugins,
     stats: {