webpack.config.js 5.8 KB

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