"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); // @ts-ignore const webpack_pwa_manifest_plugin_1 = __importDefault(require("@expo/webpack-pwa-manifest-plugin")); const InterpolateHtmlPlugin_1 = __importDefault(require("react-dev-utils/InterpolateHtmlPlugin")); const webpack_1 = require("webpack"); const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer"); // @ts-ignore const webpack_deep_scope_plugin_1 = __importDefault(require("webpack-deep-scope-plugin")); const workbox_webpack_plugin_1 = __importDefault(require("workbox-webpack-plugin")); // @ts-ignore const clean_webpack_plugin_1 = __importDefault(require("clean-webpack-plugin")); // @ts-ignore const ModuleNotFoundPlugin_1 = __importDefault(require("react-dev-utils/ModuleNotFoundPlugin")); // @ts-ignore const pnp_webpack_plugin_1 = __importDefault(require("pnp-webpack-plugin")); const ModuleScopePlugin_1 = __importDefault(require("react-dev-utils/ModuleScopePlugin")); const webpack_manifest_plugin_1 = __importDefault(require("webpack-manifest-plugin")); // @ts-ignore const case_sensitive_paths_webpack_plugin_1 = __importDefault(require("case-sensitive-paths-webpack-plugin")); const WatchMissingNodeModulesPlugin_1 = __importDefault(require("react-dev-utils/WatchMissingNodeModulesPlugin")); // @ts-ignore const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin")); // @ts-ignore const compression_webpack_plugin_1 = __importDefault(require("compression-webpack-plugin")); // @ts-ignore const brotli_webpack_plugin_1 = __importDefault(require("brotli-webpack-plugin")); const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin")); const paths_1 = require("./utils/paths"); const plugins_1 = require("./plugins"); const utils_1 = require("./utils"); const config_1 = require("./utils/config"); const createFontLoader_1 = __importDefault(require("./loaders/createFontLoader")); const createBabelLoader_1 = __importDefault(require("./loaders/createBabelLoader")); const getMode_1 = __importDefault(require("./utils/getMode")); const getConfig_1 = __importDefault(require("./utils/getConfig")); const DEFAULT_GZIP = { test: /\.(js|css)$/, filename: '[path].gz[query]', algorithm: 'gzip', threshold: 1024, minRatio: 0.8, }; const DEFAULT_BROTLI = { asset: '[path].br[query]', test: /\.(js|css)$/, threshold: 1024, minRatio: 0.8, }; const DEFAULT_SERVICE_WORKER = {}; const DEFAULT_REPORT_CONFIG = { verbose: false, path: 'web-report', statsFilename: 'stats.json', reportFilename: 'report.html', }; // This is needed for webpack to import static images in JavaScript files. // "url" loader works like "file" loader except that it embeds assets // smaller than specified limit in bytes as data URLs to avoid requests. // A missing `test` is equivalent to a match. // TODO: Bacon: Move SVG const imageLoaderConfiguration = { test: /\.(gif|jpe?g|png|svg)$/, use: { loader: require.resolve('url-loader'), options: { // Inline resources as Base64 when there is less reason to parallelize their download. The // heuristic we use is whether the resource would fit within a TCP/IP packet that we would // send to request the resource. // // An Ethernet MTU is usually 1500. IP headers are 20 (v4) or 40 (v6) bytes and TCP // headers are 40 bytes. HTTP response headers vary and are around 400 bytes. This leaves // about 1000 bytes for content to fit in a packet. limit: 1000, name: 'static/media/[name].[hash:8].[ext]', }, }, }; // "file" loader makes sure those assets get served by WebpackDevServer. // When you `import` an asset, you get its (virtual) filename. // In production, they would get copied to the `build` folder. // This loader doesn't use a "test" so it will catch all modules // that fall through the other loaders. const fallbackLoaderConfiguration = { loader: require.resolve('file-loader'), // Exclude `js` files to keep "css" loader working as it injects // its runtime that would otherwise be processed through "file" loader. // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. // Excludes: js, jsx, ts, tsx, html, json exclude: [/\.jsx?$/, /\.tsx?$/, /\.html$/, /\.json$/], options: { name: 'static/media/[name].[hash:8].[ext]', }, }; function createNoJSComponent(message) { // from twitter.com return `" <form action="location.reload()" method="POST" style="background-color:#fff;position:fixed;top:0;left:0;right:0;bottom:0;z-index:9999;"><div style="font-size:18px;font-family:Helvetica,sans-serif;line-height:24px;margin:10%;width:80%;"> <p>${message}</p> <p style="margin:20px 0;"> <button type="submit" style="background-color: #4630EB; border-radius: 100px; border: none; box-shadow: none; color: #fff; cursor: pointer; font-weight: bold; line-height: 20px; padding: 6px 16px;">Reload</button> </p> </div> </form> "`; } function getDevtool({ production, development }, { devtool }) { if (production) { // string or false if (devtool !== undefined) { // When big assets are involved sources maps can become expensive and cause your process to run out of memory. return devtool; } return 'source-map'; } if (development) { return 'cheap-module-source-map'; } return false; } function default_1(env) { return __awaiter(this, void 0, void 0, function* () { const config = getConfig_1.default(env); const mode = getMode_1.default(env); const isDev = mode === 'development'; const isProd = mode === 'production'; const { platform = 'web' } = env; // Enables deep scope analysis in production mode. // Remove unused import/exports // override: `env.removeUnusedImportExports` const deepScopeAnalysisEnabled = config_1.overrideWithPropertyOrConfig(env.removeUnusedImportExports, false // isProd ); const locations = env.locations || (yield paths_1.getPathsAsync(env.projectRoot)); const { publicPath, publicUrl } = paths_1.getPublicPaths(env); const middlewarePlugins = []; const { build: buildConfig = {}, lang } = config.web; const { rootId, babel: babelAppConfig = {} } = buildConfig; const { noJavaScriptMessage } = config.web.dangerous; const noJSComponent = createNoJSComponent(noJavaScriptMessage); if (deepScopeAnalysisEnabled) { // @ts-ignore middlewarePlugins.push(new webpack_deep_scope_plugin_1.default()); } const serviceWorker = config_1.overrideWithPropertyOrConfig( // Prevent service worker in development mode buildConfig.serviceWorker, DEFAULT_SERVICE_WORKER); if (serviceWorker) { // Generate a service worker script that will precache, and keep up to date, // the HTML & assets that are part of the Webpack build. middlewarePlugins.push(new workbox_webpack_plugin_1.default.GenerateSW(Object.assign({ exclude: [ /\.LICENSE$/, /\.map$/, /asset-manifest\.json$/, // Exclude all apple touch images as they are cached locally after the PWA is added. /^\bapple.*\.png$/, ], /// SINGLE PAGE: navigateFallback: `${publicUrl}/index.html`, clientsClaim: true, importWorkboxFrom: 'cdn', navigateFallbackBlacklist: [ // Exclude URLs starting with /_, as they're likely an API call new RegExp('^/_'), // Exclude URLs containing a dot, as they're likely a resource in // public/ and not a SPA route new RegExp('/[^/]+\\.[^/]+$'), ] }, (isDev ? { include: [], } : {}), serviceWorker))); } /** * report: { * verbose: false, * path: "web-report", * statsFilename: "stats.json", * reportFilename: "report.html" * } */ let reportPlugins = []; const reportConfig = config_1.enableWithPropertyOrConfig(env.report, DEFAULT_REPORT_CONFIG, true); if (typeof buildConfig.report !== 'undefined') { throw new Error('expo.web.build.report is deprecated. Please extend webpack.config.js and use env.report instead.'); } if (reportConfig) { if (isDev && reportConfig.verbose) { console.log('Generating a report, this will add noticeably more time to rebuilds.'); } const reportDir = reportConfig.path; reportPlugins = [ // Delete the report folder new clean_webpack_plugin_1.default([locations.absolute(reportDir)], { root: locations.root, dry: false, verbose: reportConfig.verbose, }), // Generate the report.html and stats.json new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(Object.assign({ analyzerMode: 'static', defaultSizes: 'gzip', generateStatsFile: true, openAnalyzer: false }, reportConfig, { logLevel: reportConfig.verbose ? 'info' : 'silent', statsFilename: locations.absolute(reportDir, reportConfig.statsFilename), reportFilename: locations.absolute(reportDir, reportConfig.reportFilename) })), ]; } const devtool = getDevtool({ production: isProd, development: isDev }, buildConfig); const babelProjectRoot = babelAppConfig.root || locations.root; const babelLoader = createBabelLoader_1.default({ mode, platform, babelProjectRoot, verbose: babelAppConfig.verbose, include: babelAppConfig.include, use: babelAppConfig.use, }); const allLoaders = [ { test: /\.html$/, use: [require.resolve('html-loader')], exclude: locations.template.folder, }, imageLoaderConfiguration, babelLoader, createFontLoader_1.default({ locations }), { test: /\.(css)$/, use: [require.resolve('style-loader'), require.resolve('css-loader')], }, // This needs to be the last loader fallbackLoaderConfiguration, ].filter(Boolean); /** * web: { * build: { * verbose: boolean, * brotli: boolean | {}, // (Brotli Options) * gzip: boolean | CompressionPlugin.Options<O>, * } * } */ const gzipConfig = isProd && config_1.overrideWithPropertyOrConfig(buildConfig.gzip, DEFAULT_GZIP); const brotliConfig = isProd && config_1.enableWithPropertyOrConfig(buildConfig.brotli, DEFAULT_BROTLI); const appEntry = []; // In solutions like Gatsby the main entry point doesn't need to be known. if (locations.appMain) { appEntry.push(locations.appMain); } else { throw new Error(`The entry point for your project couldn't be found. Please define it in the package.json main field`); } if (isDev) { // https://github.com/facebook/create-react-app/blob/e59e0920f3bef0c2ac47bbf6b4ff3092c8ff08fb/packages/react-scripts/config/webpack.config.js#L144 // Include an alternative client for WebpackDevServer. A client's job is to // connect to WebpackDevServer by a socket and get notified about changes. // When you save a file, the client will either apply hot updates (in case // of CSS changes), or refresh the page (in case of JS changes). When you // make a syntax error, this client will display a syntax error overlay. // Note: instead of the default WebpackDevServer client, we use a custom one // to bring better experience for Create React App users. You can replace // the line below with these two lines if you prefer the stock client: // require.resolve('webpack-dev-server/client') + '?/', // require.resolve('webpack/hot/dev-server'), appEntry.unshift(require.resolve('react-dev-utils/webpackHotDevClient')); } return { mode, entry: { app: appEntry, }, // https://webpack.js.org/configuration/other-options/#bail // Fail out on the first error instead of tolerating it. bail: isProd, devtool, context: __dirname, // configures where the build ends up output: { // Build folder (default `web-build`) path: locations.production.folder, sourceMapFilename: '[chunkhash].map', // We inferred the "public path" (such as / or /my-project) from homepage. // We use "/" in development. publicPath, }, plugins: [ // Delete the build folder isProd && new clean_webpack_plugin_1.default([locations.production.folder], { root: locations.root, dry: false, verbose: false, }), // Copy the template files over isProd && new copy_webpack_plugin_1.default([ { from: locations.template.folder, to: locations.production.folder, // We generate new versions of these based on the templates ignore: ['favicon.ico', 'serve.json', 'index.html', 'icon.png'], }, { from: locations.template.serveJson, to: locations.production.serveJson, }, { from: locations.template.favicon, to: locations.production.favicon, }, ]), // Generate the `index.html` new plugins_1.ExpoHtmlWebpackPlugin(env), // Add variables to the `index.html` new InterpolateHtmlPlugin_1.default(plugins_1.ExpoHtmlWebpackPlugin, { WEB_PUBLIC_URL: publicPath, WEB_TITLE: config.web.name, NO_SCRIPT: noJSComponent, LANG_ISO_CODE: lang, ROOT_ID: rootId, }), new webpack_pwa_manifest_plugin_1.default(config, { publicPath, noResources: isDev || !env.pwa, filename: locations.production.manifest, HtmlWebpackPlugin: plugins_1.ExpoHtmlWebpackPlugin, }), // This gives some necessary context to module not found errors, such as // the requesting resource. new ModuleNotFoundPlugin_1.default(locations.root), new plugins_1.ExpoDefinePlugin({ mode, publicUrl, config, productionManifestPath: locations.production.manifest, }), // This is necessary to emit hot updates (currently CSS only): isDev && new webpack_1.HotModuleReplacementPlugin(), // Watcher doesn't work well if you mistype casing in a path so we use // a plugin that prints an error when you attempt to do this. // See https://github.com/facebook/create-react-app/issues/240 isDev && new case_sensitive_paths_webpack_plugin_1.default(), // If you require a missing module and then `npm install` it, you still have // to restart the development server for Webpack to discover it. This plugin // makes the discovery automatic so you don't have to restart. // See https://github.com/facebook/create-react-app/issues/186 isDev && new WatchMissingNodeModulesPlugin_1.default(locations.modules), isProd && new mini_css_extract_plugin_1.default({ // Options similar to the same options in webpackOptions.output // both options are optional filename: 'static/css/[name].[contenthash:8].css', chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', }), // Generate a manifest file which contains a mapping of all asset filenames // to their corresponding output file so that tools can pick it up without // having to parse `index.html`. new webpack_manifest_plugin_1.default({ fileName: 'asset-manifest.json', publicPath, }), ...middlewarePlugins, gzipConfig && new compression_webpack_plugin_1.default(gzipConfig), brotliConfig && new brotli_webpack_plugin_1.default(brotliConfig), new plugins_1.ExpoProgressBarPlugin(), ...reportPlugins, ].filter(Boolean), module: { strictExportPresence: false, rules: [ // Disable require.ensure because it breaks tree shaking. { parser: { requireEnsure: false } }, { oneOf: allLoaders, }, ].filter(Boolean), }, resolveLoader: { plugins: [ // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders // from the current package. pnp_webpack_plugin_1.default.moduleLoader(module), ], }, resolve: { alias: config_1.DEFAULT_ALIAS, extensions: utils_1.getModuleFileExtensions('web'), plugins: [ // Adds support for installing with Plug'n'Play, leading to faster installs and adding // guards against forgotten dependencies and such. pnp_webpack_plugin_1.default, // Prevents users from importing files from outside of node_modules/. // This often causes confusion because we only process files within the root folder with babel. // To fix this, we prevent you from importing files out of the root folder -- if you'd like to, // please link the files into your node_modules/ and let module-resolution kick in. // Make sure your source files are compiled, as they will not be processed in any way. new ModuleScopePlugin_1.default(babelProjectRoot, [locations.packageJson]), ], symlinks: false, }, // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { module: 'empty', dgram: 'empty', dns: 'mock', fs: 'empty', http2: 'empty', net: 'empty', tls: 'empty', child_process: 'empty', }, }; }); } exports.default = default_1; //# sourceMappingURL=webpack.common.js.map