webpack.config.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. const path = require('path');
  2. const webpack = require('webpack');
  3. const CopyPlugin = require('copy-webpack-plugin');
  4. const ManifestPlugin = require('webpack-manifest-plugin');
  5. const VersionPlugin = require('./build/version_plugin');
  6. const AndroidIndexPlugin = require('./build/android_index_plugin');
  7. const ExtractTextPlugin = require('extract-text-webpack-plugin');
  8. const webJsOptions = {
  9. babelrc: false,
  10. presets: [
  11. [
  12. '@babel/preset-env',
  13. {
  14. useBuiltIns: 'entry'
  15. }
  16. ]
  17. ],
  18. plugins: [
  19. '@babel/plugin-syntax-dynamic-import',
  20. 'module:nanohtml',
  21. ['@babel/plugin-proposal-class-properties', { loose: false }]
  22. ]
  23. };
  24. const serviceWorker = {
  25. target: 'webworker',
  26. entry: {
  27. serviceWorker: './app/serviceWorker.js'
  28. },
  29. output: {
  30. filename: '[name].js',
  31. path: path.resolve(__dirname, 'dist'),
  32. publicPath: '/'
  33. },
  34. devtool: 'source-map',
  35. module: {
  36. rules: [
  37. {
  38. test: /\.(png|jpg)$/,
  39. loader: 'file-loader',
  40. options: {
  41. name: '[name].[hash:8].[ext]'
  42. }
  43. },
  44. {
  45. test: /\.svg$/,
  46. use: [
  47. {
  48. loader: 'file-loader',
  49. options: {
  50. name: '[name].[hash:8].[ext]'
  51. }
  52. },
  53. {
  54. loader: 'svgo-loader',
  55. options: {
  56. plugins: [
  57. { removeViewBox: false }, // true causes stretched images
  58. { convertStyleToAttrs: true }, // for CSP, no unsafe-eval
  59. { removeTitle: true } // for smallness
  60. ]
  61. }
  62. }
  63. ]
  64. },
  65. {
  66. // loads all assets from assets/ for use by common/assets.js
  67. test: require.resolve('./build/generate_asset_map.js'),
  68. use: ['babel-loader', 'val-loader']
  69. }
  70. ]
  71. },
  72. plugins: [new webpack.IgnorePlugin(/\.\.\/dist/)]
  73. };
  74. const web = {
  75. target: 'web',
  76. entry: {
  77. app: ['./app/main.js'],
  78. android: ['./android/android.js'],
  79. ios: ['./ios/ios.js']
  80. },
  81. output: {
  82. filename: '[name].[hash:8].js',
  83. path: path.resolve(__dirname, 'dist')
  84. },
  85. module: {
  86. rules: [
  87. {
  88. test: /\.js$/,
  89. oneOf: [
  90. {
  91. include: [require.resolve('./app/cryptofill')],
  92. use: ['script-loader']
  93. },
  94. {
  95. loader: 'babel-loader',
  96. include: [
  97. path.resolve(__dirname, 'app'),
  98. path.resolve(__dirname, 'common'),
  99. // some dependencies need to get re-babeled because we
  100. // have different targets than their default configs
  101. path.resolve(__dirname, 'node_modules/testpilot-ga/src'),
  102. path.resolve(__dirname, 'node_modules/fluent'),
  103. path.resolve(__dirname, 'node_modules/fluent-intl-polyfill'),
  104. path.resolve(__dirname, 'node_modules/intl-pluralrules')
  105. ],
  106. options: webJsOptions
  107. },
  108. {
  109. // Strip asserts from our deps, mainly choojs family
  110. include: [path.resolve(__dirname, 'node_modules')],
  111. exclude: [
  112. path.resolve(__dirname, 'node_modules/crc'),
  113. path.resolve(__dirname, 'node_modules/fluent')
  114. ],
  115. loader: 'webpack-unassert-loader'
  116. }
  117. ]
  118. },
  119. {
  120. test: /\.(png|jpg)$/,
  121. loader: 'file-loader',
  122. options: {
  123. name: '[name].[hash:8].[ext]'
  124. }
  125. },
  126. {
  127. test: /\.svg$/,
  128. use: [
  129. {
  130. loader: 'file-loader',
  131. options: {
  132. name: '[name].[hash:8].[ext]'
  133. }
  134. },
  135. {
  136. loader: 'svgo-loader',
  137. options: {
  138. plugins: [
  139. { removeViewBox: false }, // true causes stretched images
  140. { convertStyleToAttrs: true }, // for CSP, no unsafe-eval
  141. { removeTitle: true } // for smallness
  142. ]
  143. }
  144. }
  145. ]
  146. },
  147. {
  148. // creates style.css with all styles
  149. test: /\.css$/,
  150. use: ExtractTextPlugin.extract({
  151. use: [
  152. {
  153. loader: 'css-loader',
  154. options: {
  155. importLoaders: 1
  156. }
  157. },
  158. 'postcss-loader'
  159. ]
  160. })
  161. },
  162. {
  163. test: /\.ftl$/,
  164. use: 'raw-loader'
  165. },
  166. {
  167. // creates test.js for /test
  168. test: require.resolve('./test/frontend/index.js'),
  169. use: ['babel-loader', 'val-loader']
  170. },
  171. {
  172. // loads all assets from assets/ for use by common/assets.js
  173. test: require.resolve('./build/generate_asset_map.js'),
  174. use: ['babel-loader', 'val-loader']
  175. }
  176. ]
  177. },
  178. plugins: [
  179. new CopyPlugin([
  180. {
  181. context: 'public',
  182. from: '*.*'
  183. }
  184. ]),
  185. new webpack.EnvironmentPlugin(['NODE_ENV']),
  186. new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
  187. new ExtractTextPlugin({
  188. filename: '[name].[hash:8].css'
  189. }),
  190. new VersionPlugin(), // used for the /__version__ route
  191. new AndroidIndexPlugin(),
  192. new ManifestPlugin() // used by server side to resolve hashed assets
  193. ],
  194. devtool: 'source-map',
  195. devServer: {
  196. before:
  197. process.env.NODE_ENV === 'development' && require('./server/bin/dev'),
  198. compress: true,
  199. hot: false,
  200. host: '0.0.0.0',
  201. proxy: {
  202. '/api/ws': {
  203. target: 'ws://localhost:8081',
  204. ws: true,
  205. secure: false
  206. }
  207. }
  208. }
  209. };
  210. module.exports = (env, argv) => {
  211. const mode = argv.mode || 'production';
  212. console.error(`mode: ${mode}`);
  213. process.env.NODE_ENV = web.mode = serviceWorker.mode = mode;
  214. if (mode === 'development') {
  215. // istanbul instruments the source for code coverage
  216. webJsOptions.plugins.push('istanbul');
  217. web.entry.tests = ['./test/frontend/index.js'];
  218. }
  219. return [web, serviceWorker];
  220. };