webpack.common.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*************************************************************
  2. *
  3. * Copyright (c) 2018 The MathJax Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /**
  18. * @fileoverview Creates configurations for webpacking of MathJax components
  19. *
  20. * @author dpvc@mathjax.org (Davide Cervone)
  21. */
  22. const fs = require('fs');
  23. const path = require('path');
  24. const webpack = require('webpack');
  25. const TerserPlugin = require('terser-webpack-plugin');
  26. /**************************************************************/
  27. /**
  28. * @param {string} string The string whose special characters are to be escaped
  29. * @return {string} The string with regex special characters escaped
  30. */
  31. function quoteRE(string) {
  32. return string.replace(/([\\.{}[\]()?*^$])/g, '\\$1')
  33. }
  34. /**
  35. * Creates the plugin needed for including jsdir in the output
  36. *
  37. * @param {string} js The location of the compiled js files
  38. * @param {string} dir The directory of the component being built
  39. * @return {any[]} The plugin array (empty or with the conversion plugin)
  40. */
  41. const PLUGINS = function (js, dir) {
  42. const mjdir = path.resolve(__dirname, '..', 'js');
  43. const jsdir = path.resolve(dir, js);
  44. //
  45. // Record the js directory for the pack command
  46. //
  47. return [new webpack.DefinePlugin({
  48. __JSDIR__: jsdir
  49. })];
  50. };
  51. /**
  52. * Creates the plugin needed for converting mathjax references to component/lib references
  53. *
  54. * @param {string} js The location of the compiled js files
  55. * @param {string[]} lib The component library directories to be linked against
  56. * @param {string} dir The directory of the component being built
  57. * @return {any[]} The plugin array (empty or with the conversion plugin)
  58. */
  59. const RESOLVE = function (js, libs, dir) {
  60. const mjdir = path.resolve(__dirname, '..', 'js');
  61. const jsdir = path.resolve(dir, js);
  62. const mjRE = new RegExp('^(?:' + quoteRE(jsdir) + '|' + quoteRE(mjdir) + ')' + quoteRE(path.sep));
  63. const root = path.dirname(mjdir);
  64. //
  65. // Add directory names to libraries
  66. //
  67. libs = libs.map(lib => path.join(lib.charAt(0) === '.' ? dir : root, lib) + path.sep);
  68. //
  69. // Function replace imported files by ones in the specified component lib directories.
  70. //
  71. const replaceLibs = (resource) => {
  72. //
  73. // The full file name to check.
  74. //
  75. const request = require.resolve(
  76. resource.request ?
  77. resource.request.charAt(0) === '.' ? path.resolve(resource.path, resource.request) : resource.request :
  78. resource.path
  79. );
  80. //
  81. // Only check files in the MathJax js directory.
  82. //
  83. if (!request.match(mjRE)) return;
  84. //
  85. // Loop through the libraries and see if the imported file is there.
  86. // If so, replace the request with the library version and return.
  87. //
  88. for (const lib of libs) {
  89. const file = request.replace(mjRE, lib);
  90. if (fs.existsSync(file)) {
  91. resource.path = file;
  92. resource.request = undefined;
  93. return;
  94. }
  95. }
  96. }
  97. //
  98. // A plugin that looks for files and modules to see if they need replacing with library versions.
  99. //
  100. class ResolveReplacementPlugin {
  101. apply(compiler) {
  102. compiler.hooks.file.tap(ResolveReplacementPlugin.name, replaceLibs);
  103. compiler.hooks.module.tap(ResolveReplacementPlugin.name, replaceLibs);
  104. }
  105. }
  106. return {plugins: [new ResolveReplacementPlugin()]};
  107. }
  108. /**
  109. * Add babel-loader to appropriate directories
  110. *
  111. * @param {string} dir The directory for the component being built
  112. * @return {any} The modules specification for the webpack configuration
  113. */
  114. const MODULE = function (dir) {
  115. //
  116. // Only need to transpile our directory and components directory
  117. //
  118. const dirRE = (dir.substr(0, __dirname.length) === __dirname ? quoteRE(__dirname) :
  119. '(?:' + quoteRE(__dirname) + '|' + quoteRE(dir) + ')');
  120. return {
  121. // NOTE: for babel transpilation
  122. rules: [{
  123. test: new RegExp(dirRE + quoteRE(path.sep) + '.*\\.js$'),
  124. exclude: new RegExp(quoteRE(path.join(path.dirname(__dirname), 'es5') + path.sep)),
  125. use: {
  126. loader: 'babel-loader',
  127. options: {
  128. presets: ['@babel/env']
  129. }
  130. }
  131. }]
  132. }
  133. };
  134. /**
  135. * Create a webpack configuration for a distribution file
  136. *
  137. * @param {string} name The name of the component to create
  138. * @param {string} js The path to the compiled .js files
  139. * @param {string[]} libs Array of paths to component lib directories to link against
  140. * @param {string} dir The directory of the component buing built
  141. * @param {string} dist The path to the directory where the component .js file will be placed
  142. * (defaults to es5 in the same directory as the js directory)
  143. */
  144. const PACKAGE = function (name, js, libs, dir, dist) {
  145. const distDir = dist ? path.resolve(dir, dist) :
  146. path.resolve(path.dirname(js), 'es5', path.dirname(name));
  147. name = path.basename(name);
  148. return {
  149. name: name,
  150. entry: path.join(dir, name + '.js'),
  151. output: {
  152. path: distDir,
  153. filename: name + (dist === '.' ? '.min.js' : '.js')
  154. },
  155. target: ['web', 'es5'], // needed for IE11 and old browsers
  156. plugins: PLUGINS(js, dir),
  157. resolve: RESOLVE(js, libs, dir),
  158. module: MODULE(dir),
  159. performance: {
  160. hints: false
  161. },
  162. optimization: {
  163. minimize: true,
  164. minimizer: [new TerserPlugin({
  165. extractComments: false,
  166. terserOptions: {
  167. output: {
  168. ascii_only: true
  169. }
  170. }
  171. })]
  172. },
  173. mode: 'production'
  174. };
  175. }
  176. module.exports = PACKAGE;