webpack.config.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. const path = require("path");
  2. const HtmlWebpackPlugin = require("html-webpack-plugin");
  3. const webpack = require("webpack");
  4. const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
  5. const dotenv = require("dotenv");
  6. const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  7. .BundleAnalyzerPlugin;
  8. const TerserPlugin = require("terser-webpack-plugin");
  9. module.exports = () => {
  10. let env = dotenv.config().parsed;
  11. if (!env) {
  12. env = process.env;
  13. }
  14. const envKeys = Object.keys(env).reduce((prev, next) => {
  15. const varName = `process.env.${next}`;
  16. if (typeof env[next] !== "string") return prev;
  17. if (env[next].toLowerCase() === "true") {
  18. prev[varName] = true;
  19. } else if (env[next].toLowerCase() === "false") {
  20. prev[varName] = false;
  21. } else {
  22. prev[varName] = JSON.stringify(env[next]);
  23. }
  24. return prev;
  25. }, {});
  26. // Check first the env file and if it's empty, check out the node env of the process.
  27. let isDevelopment = env.NODE_ENV !== "production";
  28. if (process.env.NODE_ENV !== env.NODE_ENV) {
  29. isDevelopment = process.env.NODE_ENV !== "production";
  30. }
  31. let htmlPluginOpts = {
  32. template: path.resolve(__dirname, "src", "index.html"),
  33. };
  34. if (env.IS_HOSTED) {
  35. htmlPluginOpts = {
  36. template: path.resolve(__dirname, "src", "hosted.index.html"),
  37. intercomAppId: `${env.INTERCOM_APP_ID}`,
  38. hotjarId: `${env.HOTJAR_ID}`,
  39. intercomSrc: `${process.env.INTERCOM_SRC}`,
  40. segmentWriteKey: `${process.env.SEGMENT_WRITE_KEY}`,
  41. segmentKey: `${process.env.SEGMENT_PUBLIC_KEY}`,
  42. };
  43. }
  44. /**
  45. * @type {webpack.Configuration}
  46. */
  47. const config = {
  48. entry: [
  49. "core-js/modules/es.promise",
  50. "core-js/modules/es.array.iterator",
  51. "./src/index.tsx",
  52. ],
  53. target: "web",
  54. mode: isDevelopment ? "development" : "production",
  55. devtool: "source-map",
  56. module: {
  57. rules: [
  58. {
  59. test: /\.(ts|tsx|mjs|js|jsx)$/,
  60. exclude: /node_modules/,
  61. use: [
  62. {
  63. loader: require.resolve("babel-loader"),
  64. options: {
  65. plugins: [
  66. isDevelopment && require.resolve("react-refresh/babel"),
  67. ].filter(Boolean),
  68. },
  69. },
  70. ],
  71. },
  72. {
  73. enforce: "pre",
  74. test: /\.js$/,
  75. loader: "source-map-loader",
  76. },
  77. {
  78. test: /\.(png|svg|jpg|gif|mp3)$/,
  79. use: ["file-loader"],
  80. },
  81. // {
  82. // test: /\.css$/i,
  83. // loader: "css-loader",
  84. // options: {
  85. // import: true,
  86. // },
  87. // },
  88. {
  89. test: /\.css$/i,
  90. use: [
  91. { loader: "style-loader", options: { injectType: "linkTag" } },
  92. { loader: "file-loader" },
  93. ],
  94. },
  95. {
  96. test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
  97. use: [
  98. {
  99. loader: "file-loader",
  100. options: {
  101. name: "[name].[ext]",
  102. outputPath: "fonts/",
  103. },
  104. },
  105. ],
  106. },
  107. ],
  108. },
  109. resolve: {
  110. modules: [path.resolve(__dirname, "src"), "node_modules"],
  111. extensions: ["*", ".tsx", ".ts", ".js", ".jsx", ".json"],
  112. },
  113. output: {
  114. filename: "bundle.js",
  115. path: path.resolve(__dirname, "build"),
  116. publicPath: "/",
  117. },
  118. devServer: {
  119. historyApiFallback: true,
  120. disableHostCheck: true,
  121. host: "0.0.0.0",
  122. port: env.DEV_SERVER_PORT || 8080,
  123. hot: true,
  124. },
  125. plugins: [
  126. new HtmlWebpackPlugin(htmlPluginOpts),
  127. new webpack.DefinePlugin(envKeys),
  128. isDevelopment && new ReactRefreshWebpackPlugin(),
  129. ].filter(Boolean),
  130. };
  131. if (!isDevelopment) {
  132. config.optimization = {
  133. minimize: true,
  134. minimizer: [
  135. new TerserPlugin({
  136. test: /\.(ts|tsx|mjs|js|jsx)$/,
  137. terserOptions: {
  138. parse: {
  139. // We want terser to parse ecma 8 code. However, we don't want it
  140. // to apply minification steps that turns valid ecma 5 code
  141. // into invalid ecma 5 code. This is why the `compress` and `output`
  142. ecma: 8,
  143. },
  144. compress: {
  145. ecma: 5,
  146. warnings: false,
  147. inline: 2,
  148. },
  149. mangle: {
  150. // Find work around for Safari 10+
  151. safari10: true,
  152. },
  153. output: {
  154. ecma: 5,
  155. comments: false,
  156. ascii_only: true,
  157. },
  158. },
  159. // Use multi-process parallel running to improve the build speed
  160. parallel: true,
  161. }),
  162. ],
  163. };
  164. }
  165. if (env.ENABLE_ANALYZER) {
  166. config.plugins.push(new BundleAnalyzerPlugin());
  167. }
  168. if (env.ENABLE_PROXY) {
  169. if (!env.API_SERVER) {
  170. throw new Error(
  171. "API_SERVER is not present on .env! Please setup the api server url if you want the proxy to work! API_SERVER example: http://localhost:8080"
  172. );
  173. }
  174. config.devServer.proxy = {
  175. "/api": {
  176. logLevel: "debug",
  177. target: env.API_SERVER, // target host
  178. changeOrigin: true, // needed for virtual hosted sites
  179. ws: true, // proxy websockets
  180. },
  181. };
  182. }
  183. return config;
  184. };