webpack.config.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. var plib = require('path');
  6. var fs = require('fs-extra');
  7. var Handlebars = require('handlebars');
  8. var HtmlWebpackPlugin = require('html-webpack-plugin');
  9. var webpack = require('webpack');
  10. var DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin');
  11. var BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
  12. .BundleAnalyzerPlugin;
  13. var Build = require('@jupyterlab/buildutils').Build;
  14. var WPPlugin = require('@jupyterlab/buildutils').WPPlugin;
  15. var package_data = require('./package.json');
  16. // Handle the extensions.
  17. var jlab = package_data.jupyterlab;
  18. var extensions = jlab.extensions;
  19. var mimeExtensions = jlab.mimeExtensions;
  20. var packageNames = Object.keys(mimeExtensions).concat(Object.keys(extensions));
  21. // Ensure a clear build directory.
  22. var buildDir = plib.resolve(jlab.buildDir);
  23. if (fs.existsSync(buildDir)) {
  24. fs.removeSync(buildDir);
  25. }
  26. fs.ensureDirSync(buildDir);
  27. // Build the assets
  28. var extraConfig = Build.ensureAssets({
  29. packageNames: packageNames,
  30. output: jlab.outputDir
  31. });
  32. // Create the entry point file.
  33. var source = fs.readFileSync('index.js').toString();
  34. var template = Handlebars.compile(source);
  35. var data = {
  36. jupyterlab_extensions: extensions,
  37. jupyterlab_mime_extensions: mimeExtensions
  38. };
  39. var result = template(data);
  40. fs.writeFileSync(plib.join(buildDir, 'index.out.js'), result);
  41. fs.copySync('./package.json', plib.join(buildDir, 'package.json'));
  42. fs.copySync(
  43. plib.join(jlab.outputDir, 'imports.css'),
  44. plib.join(buildDir, 'imports.css')
  45. );
  46. // Set up variables for the watch mode ignore plugins
  47. let watched = {};
  48. let ignoreCache = Object.create(null);
  49. Object.keys(jlab.linkedPackages).forEach(function(name) {
  50. if (name in watched) return;
  51. const localPkgPath = require.resolve(plib.join(name, 'package.json'));
  52. watched[name] = plib.dirname(localPkgPath);
  53. });
  54. /**
  55. * Sync a local path to a linked package path if they are files and differ.
  56. */
  57. function maybeSync(localPath, name, rest) {
  58. const stats = fs.statSync(localPath);
  59. if (!stats.isFile(localPath)) {
  60. return;
  61. }
  62. const source = fs.realpathSync(plib.join(jlab.linkedPackages[name], rest));
  63. if (source === fs.realpathSync(localPath)) {
  64. return;
  65. }
  66. fs.watchFile(source, { interval: 500 }, function(curr) {
  67. if (!curr || curr.nlink === 0) {
  68. return;
  69. }
  70. try {
  71. fs.copySync(source, localPath);
  72. } catch (err) {
  73. console.error(err);
  74. }
  75. });
  76. }
  77. /**
  78. * A filter function set up to exclude all files that are not
  79. * in a package contained by the Jupyterlab repo. Used to ignore
  80. * files during a `--watch` build.
  81. */
  82. function ignored(path) {
  83. path = plib.resolve(path);
  84. if (path in ignoreCache) {
  85. // Bail if already found.
  86. return ignoreCache[path];
  87. }
  88. // Limit the watched files to those in our local linked package dirs.
  89. let ignore = true;
  90. Object.keys(watched).some(name => {
  91. const rootPath = watched[name];
  92. const contained = path.indexOf(rootPath + plib.sep) !== -1;
  93. if (path !== rootPath && !contained) {
  94. return false;
  95. }
  96. const rest = path.slice(rootPath.length);
  97. if (rest.indexOf('node_modules') === -1) {
  98. ignore = false;
  99. maybeSync(path, name, rest);
  100. }
  101. return true;
  102. });
  103. ignoreCache[path] = ignore;
  104. return ignore;
  105. }
  106. const plugins = [
  107. new DuplicatePackageCheckerPlugin({
  108. verbose: true,
  109. exclude(instance) {
  110. // ignore known duplicates
  111. return ['domelementtype', 'hash-base', 'inherits'].includes(
  112. instance.name
  113. );
  114. }
  115. }),
  116. new HtmlWebpackPlugin({
  117. chunksSortMode: 'none',
  118. template: plib.join('templates', 'template.html'),
  119. title: jlab.name || 'JupyterLab'
  120. }),
  121. new webpack.HashedModuleIdsPlugin(),
  122. // custom plugin for ignoring files during a `--watch` build
  123. new WPPlugin.FilterWatchIgnorePlugin(ignored),
  124. // custom plugin that copies the assets to the static directory
  125. new WPPlugin.FrontEndPlugin(buildDir, jlab.staticDir)
  126. ];
  127. if (process.argv.includes('--analyze')) {
  128. plugins.push(new BundleAnalyzerPlugin());
  129. }
  130. module.exports = [
  131. {
  132. mode: 'development',
  133. entry: {
  134. main: ['whatwg-fetch', plib.resolve(buildDir, 'index.out.js')]
  135. },
  136. output: {
  137. path: plib.resolve(buildDir),
  138. publicPath: '{{page_config.fullStaticUrl}}/',
  139. filename: '[name].[chunkhash].js'
  140. },
  141. optimization: {
  142. splitChunks: {
  143. chunks: 'all'
  144. }
  145. },
  146. module: {
  147. rules: [
  148. { test: /\.css$/, use: ['style-loader', 'css-loader'] },
  149. { test: /\.md$/, use: 'raw-loader' },
  150. { test: /\.txt$/, use: 'raw-loader' },
  151. {
  152. test: /\.js$/,
  153. use: ['source-map-loader'],
  154. enforce: 'pre',
  155. // eslint-disable-next-line no-undef
  156. exclude: /node_modules/
  157. },
  158. { test: /\.(jpg|png|gif)$/, use: 'file-loader' },
  159. { test: /\.js.map$/, use: 'file-loader' },
  160. {
  161. test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
  162. use: 'url-loader?limit=10000&mimetype=application/font-woff'
  163. },
  164. {
  165. test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
  166. use: 'url-loader?limit=10000&mimetype=application/font-woff'
  167. },
  168. {
  169. test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
  170. use: 'url-loader?limit=10000&mimetype=application/octet-stream'
  171. },
  172. {
  173. test: /\.otf(\?v=\d+\.\d+\.\d+)?$/,
  174. use: 'url-loader?limit=10000&mimetype=application/octet-stream'
  175. },
  176. { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: 'file-loader' },
  177. {
  178. // in css files, svg is loaded as a url formatted string
  179. test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  180. issuer: { test: /\.css$/ },
  181. use: {
  182. loader: 'svg-url-loader',
  183. options: { encoding: 'none', limit: 10000 }
  184. }
  185. },
  186. {
  187. // in ts and tsx files (both of which compile to js),
  188. // svg is loaded as a raw string
  189. test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  190. issuer: { test: /\.js$/ },
  191. use: {
  192. loader: 'raw-loader'
  193. }
  194. }
  195. ]
  196. },
  197. watchOptions: {
  198. poll: 333
  199. },
  200. node: {
  201. fs: 'empty'
  202. },
  203. bail: true,
  204. devtool: 'inline-source-map',
  205. externals: ['node-fetch', 'ws'],
  206. plugins,
  207. stats: {
  208. chunkModules: true
  209. }
  210. }
  211. ].concat(extraConfig);