webpack.config.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. loader: 'babel-loader',
  92. include: [
  93. path.resolve(__dirname, 'app'),
  94. path.resolve(__dirname, 'common'),
  95. // some dependencies need to get re-babeled because we
  96. // have different targets than their default configs
  97. path.resolve(
  98. __dirname,
  99. 'node_modules/@dannycoates/webcrypto-liner'
  100. ),
  101. path.resolve(__dirname, 'node_modules/fluent'),
  102. path.resolve(__dirname, 'node_modules/fluent-intl-polyfill'),
  103. path.resolve(__dirname, 'node_modules/intl-pluralrules')
  104. ],
  105. options: webJsOptions
  106. },
  107. {
  108. // Strip asserts from our deps, mainly choojs family
  109. include: [path.resolve(__dirname, 'node_modules')],
  110. exclude: [
  111. path.resolve(__dirname, 'node_modules/crc'),
  112. path.resolve(__dirname, 'node_modules/fluent'),
  113. path.resolve(__dirname, 'node_modules/tslib'),
  114. path.resolve(__dirname, 'node_modules/webcrypto-core')
  115. ],
  116. loader: 'webpack-unassert-loader'
  117. }
  118. ]
  119. },
  120. {
  121. test: /\.(png|jpg)$/,
  122. loader: 'file-loader',
  123. options: {
  124. name: '[name].[hash:8].[ext]'
  125. }
  126. },
  127. {
  128. test: /\.svg$/,
  129. use: [
  130. {
  131. loader: 'file-loader',
  132. options: {
  133. name: '[name].[hash:8].[ext]'
  134. }
  135. },
  136. {
  137. loader: 'svgo-loader',
  138. options: {
  139. plugins: [
  140. { removeViewBox: false }, // true causes stretched images
  141. { convertStyleToAttrs: true }, // for CSP, no unsafe-eval
  142. { removeTitle: true } // for smallness
  143. ]
  144. }
  145. }
  146. ]
  147. },
  148. {
  149. // creates style.css with all styles
  150. test: /\.css$/,
  151. use: ExtractTextPlugin.extract({
  152. use: [
  153. {
  154. loader: 'css-loader',
  155. options: {
  156. importLoaders: 1
  157. }
  158. },
  159. 'postcss-loader'
  160. ]
  161. })
  162. },
  163. {
  164. test: /\.ftl$/,
  165. use: 'raw-loader'
  166. },
  167. {
  168. // creates test.js for /test
  169. test: require.resolve('./test/frontend/index.js'),
  170. use: ['babel-loader', 'val-loader']
  171. },
  172. {
  173. // loads all assets from assets/ for use by common/assets.js
  174. test: require.resolve('./build/generate_asset_map.js'),
  175. use: ['babel-loader', 'val-loader']
  176. }
  177. ]
  178. },
  179. plugins: [
  180. new CopyPlugin([
  181. {
  182. context: 'public',
  183. from: '*.*'
  184. }
  185. ]),
  186. new webpack.EnvironmentPlugin(['NODE_ENV']),
  187. new webpack.IgnorePlugin(/\.\.\/dist/), // used in common/*.js
  188. new ExtractTextPlugin({
  189. filename: '[name].[hash:8].css'
  190. }),
  191. new VersionPlugin(), // used for the /__version__ route
  192. new AndroidIndexPlugin(),
  193. new ManifestPlugin() // used by server side to resolve hashed assets
  194. ],
  195. devtool: 'source-map',
  196. devServer: {
  197. before:
  198. process.env.NODE_ENV === 'development' && require('./server/bin/dev'),
  199. compress: true,
  200. hot: false,
  201. host: '0.0.0.0',
  202. proxy: {
  203. '/api/ws': {
  204. target: 'ws://localhost:8081',
  205. ws: true,
  206. secure: false
  207. }
  208. }
  209. }
  210. };
  211. module.exports = (env, argv) => {
  212. const mode = argv.mode || 'production';
  213. console.error(`mode: ${mode}`);
  214. process.env.NODE_ENV = web.mode = serviceWorker.mode = mode;
  215. if (mode === 'development') {
  216. // istanbul instruments the source for code coverage
  217. webJsOptions.plugins.push('istanbul');
  218. web.entry.tests = ['./test/frontend/index.js'];
  219. }
  220. return [web, serviceWorker];
  221. };