webpack.config.js 6.2 KB

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