webpack.config.js 6.0 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 ExtractTextPlugin = require('extract-text-webpack-plugin');
  6. const IS_DEV = process.env.NODE_ENV === 'development';
  7. const regularJSOptions = {
  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 entry = {
  14. // babel-polyfill and fluent are directly included in vendor
  15. // because they are not explicitly referenced by app
  16. vendor: ['babel-polyfill', 'fluent'],
  17. app: ['./app/main.js'],
  18. style: ['./app/main.css']
  19. };
  20. if (IS_DEV) {
  21. entry.tests = ['./test/frontend/index.js'];
  22. // istanbul instruments the source for code coverage
  23. regularJSOptions.plugins.push('istanbul');
  24. }
  25. module.exports = {
  26. entry,
  27. output: {
  28. filename: '[name].[chunkhash:8].js',
  29. path: path.resolve(__dirname, 'dist'),
  30. publicPath: '/'
  31. },
  32. devtool: IS_DEV && 'inline-source-map',
  33. module: {
  34. rules: [
  35. {
  36. test: /\.js$/,
  37. oneOf: [
  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. // inlines version from package.json into header/index.js
  51. include: require.resolve('./app/templates/header'),
  52. use: [
  53. {
  54. loader: 'babel-loader',
  55. options: regularJSOptions
  56. },
  57. './build/version_loader'
  58. ]
  59. },
  60. {
  61. // fluent gets exposed as a global so that each language script
  62. // can load independently and share it.
  63. include: [path.dirname(require.resolve('fluent'))],
  64. use: [
  65. {
  66. loader: 'expose-loader',
  67. options: 'fluent'
  68. },
  69. {
  70. loader: 'babel-loader',
  71. options: {
  72. presets: [['env', { modules: false }], 'stage-3']
  73. }
  74. }
  75. ]
  76. },
  77. {
  78. loader: 'babel-loader',
  79. include: [
  80. path.resolve(__dirname, 'app'),
  81. path.resolve(__dirname, 'common'),
  82. // some dependencies need to get re-babeled because we
  83. // have different targets than their default configs
  84. path.resolve(__dirname, 'node_modules/testpilot-ga/src'),
  85. path.resolve(__dirname, 'node_modules/fluent-intl-polyfill'),
  86. path.resolve(__dirname, 'node_modules/intl-pluralrules')
  87. ],
  88. options: regularJSOptions
  89. },
  90. {
  91. // Strip asserts from our deps, mainly choojs family
  92. include: [path.resolve(__dirname, 'node_modules')],
  93. loader: 'webpack-unassert-loader'
  94. }
  95. ]
  96. },
  97. {
  98. test: /\.(png|jpg)$/,
  99. loader: 'file-loader',
  100. options: {
  101. name: '[name].[hash:8].[ext]'
  102. }
  103. },
  104. {
  105. test: /\.svg$/,
  106. use: [
  107. {
  108. loader: 'file-loader',
  109. options: {
  110. name: '[name].[hash:8].[ext]'
  111. }
  112. },
  113. {
  114. loader: 'svgo-loader',
  115. options: {
  116. plugins: [
  117. { removeViewBox: false }, // true causes stretched images
  118. { convertStyleToAttrs: true }, // for CSP, no unsafe-eval
  119. { removeTitle: true } // for smallness
  120. ]
  121. }
  122. }
  123. ]
  124. },
  125. {
  126. // creates style.css with all styles
  127. test: /\.css$/,
  128. use: ExtractTextPlugin.extract({
  129. use: [
  130. {
  131. loader: 'css-loader',
  132. options: { modules: false, importLoaders: 1 }
  133. },
  134. 'postcss-loader'
  135. ]
  136. })
  137. },
  138. {
  139. // creates version.json for /__version__ from package.json
  140. test: require.resolve('./package.json'),
  141. use: [
  142. {
  143. loader: 'file-loader',
  144. options: {
  145. name: 'version.json'
  146. }
  147. },
  148. 'extract-loader',
  149. './build/package_json_loader'
  150. ]
  151. },
  152. {
  153. // creates a js script for each ftl
  154. test: /\.ftl$/,
  155. use: [
  156. {
  157. loader: 'file-loader',
  158. options: {
  159. name: '[path][name].[hash:8].js'
  160. }
  161. },
  162. 'extract-loader',
  163. './build/fluent_loader'
  164. ]
  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. // loads all the ftl from public/locales for use by common/locales.js
  178. test: require.resolve('./build/generate_l10n_map.js'),
  179. use: ['babel-loader', 'val-loader']
  180. }
  181. ]
  182. },
  183. plugins: [
  184. new CopyPlugin([
  185. {
  186. context: 'public',
  187. from: '*.*'
  188. }
  189. ]),
  190. new webpack.IgnorePlugin(/dist/), // used in common/*.js
  191. new webpack.IgnorePlugin(/require-from-string/), // used in common/locales.js
  192. new webpack.HashedModuleIdsPlugin(),
  193. new webpack.optimize.CommonsChunkPlugin({
  194. name: 'vendor',
  195. minChunks: ({ resource }) => /node_modules/.test(resource)
  196. }),
  197. new webpack.optimize.CommonsChunkPlugin({
  198. name: 'runtime'
  199. }),
  200. new ExtractTextPlugin({
  201. filename: 'style.[contenthash:8].css'
  202. }),
  203. new ManifestPlugin() // used by server side to resolve hashed assets
  204. ],
  205. devServer: {
  206. compress: true,
  207. host: '0.0.0.0',
  208. before: IS_DEV ? require('./server/dev') : undefined
  209. }
  210. };