UNPKG

@expo/webpack-config

Version:
379 lines 18.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chalk_1 = __importDefault(require("chalk")); const clean_webpack_plugin_1 = require("clean-webpack-plugin"); const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin")); const crypto_1 = require("crypto"); const expo_pwa_1 = require("expo-pwa"); const fs_1 = __importDefault(require("fs")); const fs_extra_1 = require("fs-extra"); const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin")); const node_html_parser_1 = require("node-html-parser"); const path_1 = __importDefault(require("path")); const resolve_from_1 = __importDefault(require("resolve-from")); const webpack_manifest_plugin_1 = require("webpack-manifest-plugin"); const addons_1 = require("./addons"); const env_1 = require("./env"); const defaults_1 = require("./env/defaults"); const loaders_1 = require("./loaders"); const plugins_1 = require("./plugins"); const ModuleNotFoundPlugin_1 = require("./plugins/ModuleNotFoundPlugin"); const array_1 = require("./utils/array"); 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 defaults_1.shouldUseSourceMap ? 'source-map' : false; } if (development) { return 'cheap-module-source-map'; } return false; } function getOutput(locations, mode, publicPath, platform, port = 8081) { const commonOutput = { // We inferred the "public path" (such as / or /my-project) from homepage. // We use "/" in development. publicPath, // Build folder (default `web-build`) path: locations.production.folder, assetModuleFilename: 'static/media/[name].[hash][ext]', // Prevent chunk naming collision across platforms since // they're all coming from the same dev server. uniqueName: platform, }; if (mode === 'production') { commonOutput.filename = 'static/js/[name].[contenthash:8].js'; // There are also additional JS chunk files if you use code splitting. commonOutput.chunkFilename = 'static/js/[name].[contenthash:8].chunk.js'; // Point sourcemap entries to original disk location (format as URL on Windows) commonOutput.devtoolModuleFilenameTemplate = (info) => path_1.default.relative(locations.root, info.absoluteResourcePath).replace(/\\/g, '/'); } else { // Add comments that describe the file import/exports. // This will make it easier to debug. commonOutput.pathinfo = true; // Give the output bundle a constant name to prevent caching. // Also there are no actual files generated in dev. commonOutput.filename = 'static/js/bundle.js'; // There are also additional JS chunk files if you use code splitting. commonOutput.chunkFilename = 'static/js/[name].chunk.js'; // Point sourcemap entries to original disk location (format as URL on Windows) commonOutput.devtoolModuleFilenameTemplate = (info // TODO: revisit for web ) => path_1.default.resolve(info.absoluteResourcePath).replace(/\\/g, '/'); } return commonOutput; } function getPlatformsExtensions(platform) { return (0, env_1.getModuleFileExtensions)(platform); } function createEnvironmentHash(env) { return (0, crypto_1.createHash)('md5').update(JSON.stringify(env)).digest('hex'); } async function default_1(env, argv = {}) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; if ('report' in env) { console.warn(chalk_1.default.bgRed.black(`The \`report\` property of \`@expo/webpack-config\` is now deprecated.\nhttps://expo.fyi/webpack-report-property-is-deprecated`)); } const config = (0, env_1.getConfig)(env); const mode = (0, env_1.getMode)(env); const isDev = mode === 'development'; const isProd = mode === 'production'; const locations = env.locations || (await (0, env_1.getPathsAsync)(env.projectRoot)); const { publicPath, publicUrl } = (0, env_1.getPublicPaths)(env); const { build: buildConfig = {} } = config.web || {}; const devtool = getDevtool({ production: isProd, development: isDev }, buildConfig); const appEntry = []; // In solutions like Gatsby the main entry point doesn't need to be known. if (locations.appMain) { appEntry.push(locations.appMain); } // Add a loose requirement on the ResizeObserver polyfill if it's installed... const resizeObserverPolyfill = resolve_from_1.default.silent(env.projectRoot, 'resize-observer-polyfill/dist/ResizeObserver.global'); if (resizeObserverPolyfill) { appEntry.unshift(resizeObserverPolyfill); } let generatePWAImageAssets = !isDev; if (!isDev && typeof env.pwa !== 'undefined') { generatePWAImageAssets = env.pwa; } const filesToCopy = [ { from: locations.template.folder, to: locations.production.folder, toType: 'dir', noErrorOnMissing: true, globOptions: { dot: true, // We generate new versions of these based on the templates ignore: [ // '**/serve.json', '**/index.html', '**/icon.png', ], }, }, { from: locations.template.serveJson, to: locations.production.serveJson, }, ]; const templateIndex = (0, node_html_parser_1.parse)((0, fs_extra_1.readFileSync)(locations.template.indexHtml, { encoding: 'utf8' })); // @ts-ignore const templateLinks = templateIndex.querySelectorAll('link'); const links = templateLinks.map((value) => ({ rel: value.getAttribute('rel'), media: value.getAttribute('media'), href: value.getAttribute('href'), sizes: value.getAttribute('sizes'), node: value, })); // @ts-ignore const templateMeta = templateIndex.querySelectorAll('meta'); const meta = templateMeta.map((value) => ({ name: value.getAttribute('name'), content: value.getAttribute('content'), node: value, })); const [manifestLink] = links.filter(link => typeof link.rel === 'string' && link.rel.split(' ').includes('manifest')); let templateManifest = locations.template.manifest; // Only inject a manifest tag if the template is missing one. // This enables users to disable tag injection. const shouldInjectManifestTag = !manifestLink; if (manifestLink === null || manifestLink === void 0 ? void 0 : manifestLink.href) { // Often the manifest will be referenced as `/manifest.json` (to support public paths). // We've detected that the user has manually added a manifest tag to their template index.html which according // to our docs means they want to use a custom manifest.json instead of having a new one generated. // // Normalize the link (removing the beginning slash) so it can be resolved relative to the user's static folder. // Ref: https://docs.expo.dev/guides/progressive-web-apps/#manual-setup if (manifestLink.href.startsWith('/')) { manifestLink.href = manifestLink.href.substring(1); } templateManifest = locations.template.get(manifestLink.href); } const ensureSourceAbsolute = (input) => { if (!input) return input; return { ...input, src: locations.absolute(input.src), }; }; const emacsLockfilePattern = '**/.#*'; const allLoaders = (0, loaders_1.createAllLoaders)(env); let webpackConfig = { // Used to identify the compiler. name: env.platform, target: ['web'], watchOptions: { aggregateTimeout: 5, ignored: [ '**/.git/**', '**/node_modules/**', '**/.expo/**', '**/.expo-shared/**', '**/web-build/**', // can be removed after https://github.com/paulmillr/chokidar/issues/955 is released emacsLockfilePattern, ], }, mode, entry: appEntry, // https://webpack.js.org/configuration/other-options/#bail // Fail out on the first error instead of tolerating it. bail: isProd, devtool, // This must point to the project root (where the webpack.config.js would normally be located). // If this is anywhere else, the source maps and errors won't show correct paths. context: (_a = env.projectRoot) !== null && _a !== void 0 ? _a : __dirname, // configures where the build ends up output: getOutput(locations, mode, publicPath, env.platform, env.port), // Disable file info logs. stats: defaults_1.EXPO_DEBUG ? 'detailed' : 'errors-warnings', cache: { type: 'filesystem', version: createEnvironmentHash(process.env), cacheDirectory: path_1.default.join(env.locations.appWebpackCache, env.platform), store: 'pack', buildDependencies: { defaultWebpack: [path_1.default.join(path_1.default.dirname(require.resolve('webpack/package.json')), 'lib/')], config: [__filename], tsconfig: [env.locations.appTsConfig, env.locations.appJsConfig].filter(f => fs_1.default.existsSync(f)), }, }, infrastructureLogging: { debug: defaults_1.EXPO_DEBUG, level: defaults_1.EXPO_DEBUG ? 'verbose' : 'none', }, plugins: [ // Delete the build folder isProd && new clean_webpack_plugin_1.CleanWebpackPlugin({ dangerouslyAllowCleanPatternsOutsideProject: true, cleanOnceBeforeBuildPatterns: [locations.production.folder], dry: false, verbose: false, }), // Copy the template files over isProd && new copy_webpack_plugin_1.default({ patterns: filesToCopy }), // Generate the `index.html` new plugins_1.ExpoHtmlWebpackPlugin(env, templateIndex), plugins_1.ExpoInterpolateHtmlPlugin.fromEnv(env, plugins_1.ExpoHtmlWebpackPlugin), env.pwa && new plugins_1.ExpoPwaManifestWebpackPlugin({ template: templateManifest, path: 'manifest.json', publicPath, inject: shouldInjectManifestTag, }, config), new plugins_1.FaviconWebpackPlugin({ projectRoot: env.projectRoot, publicPath, links, }, ensureSourceAbsolute((0, expo_pwa_1.getFaviconIconConfig)(config))), generatePWAImageAssets && new plugins_1.ApplePwaWebpackPlugin({ projectRoot: env.projectRoot, publicPath, links, meta, }, { name: (_b = env.config.web) === null || _b === void 0 ? void 0 : _b.shortName, isFullScreen: (_e = (_d = (_c = env.config.web) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.apple) === null || _e === void 0 ? void 0 : _e.touchFullscreen, isWebAppCapable: !!((_h = (_g = (_f = env.config.web) === null || _f === void 0 ? void 0 : _f.meta) === null || _g === void 0 ? void 0 : _g.apple) === null || _h === void 0 ? void 0 : _h.mobileWebAppCapable), barStyle: (_l = (_k = (_j = env.config.web) === null || _j === void 0 ? void 0 : _j.meta) === null || _k === void 0 ? void 0 : _k.apple) === null || _l === void 0 ? void 0 : _l.barStyle, }, ensureSourceAbsolute((0, expo_pwa_1.getSafariIconConfig)(env.config)), ensureSourceAbsolute((0, expo_pwa_1.getSafariStartupImageConfig)(env.config))), generatePWAImageAssets && new plugins_1.ChromeIconsWebpackPlugin({ projectRoot: env.projectRoot, publicPath, }, ensureSourceAbsolute((0, expo_pwa_1.getChromeIconConfig)(config))), // This gives some necessary context to module not found errors, such as // the requesting resource. new ModuleNotFoundPlugin_1.ModuleNotFoundPlugin(locations.root), new plugins_1.ExpoDefinePlugin({ mode, publicUrl, config, // @ts-ignore platform: env.platform, }), 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 an asset manifest file with the following content: // - "files" key: Mapping of all asset filenames to their corresponding // output file so that tools can pick it up without having to parse // `index.html` // - "entrypoints" key: Array of files which are included in `index.html`, // can be used to reconstruct the HTML if necessary new webpack_manifest_plugin_1.WebpackManifestPlugin({ fileName: 'asset-manifest.json', publicPath, filter: ({ path }) => { if (path.match(/(apple-touch-startup-image|apple-touch-icon|chrome-icon|precache-manifest)/)) { return false; } // Remove compressed versions and service workers return !(path.endsWith('.gz') || path.endsWith('worker.js')); }, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { if (file.name) { manifest[file.name] = file.path; } return manifest; }, seed); const entrypointFiles = entrypoints.main.filter(fileName => !fileName.endsWith('.map')); return { files: manifestFiles, entrypoints: entrypointFiles, }; }, }), // TODO: Drop new plugins_1.ExpectedErrorsPlugin(), // Skip using a progress bar in CI env.logger && new plugins_1.ExpoProgressBarPlugin({ logger: env.logger, nonInteractive: defaults_1.isCI, bundleDetails: { bundleType: 'bundle', platform: env.platform, entryFile: locations.appMain, dev: isDev !== null && isDev !== void 0 ? isDev : false, minify: isProd !== null && isProd !== void 0 ? isProd : false, }, }), ].filter(array_1.truthy), module: { strictExportPresence: false, // @ts-ignore rules: [ // Handle node_modules packages that contain sourcemaps defaults_1.shouldUseSourceMap && { enforce: 'pre', exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, use: [ { loader: require.resolve('source-map-loader'), options: { filterSourceMappingUrl(url, resourcePath) { // https://github.com/alewin/useWorker/issues/138 if (resourcePath.match(/@koale\/useworker/)) { return 'remove'; } return true; }, }, }, ], resolve: { fullySpecified: false, }, }, { oneOf: allLoaders, }, ].filter(array_1.truthy), }, resolve: { // modules: ['node_modules'], mainFields: ['browser', 'module', 'main'], aliasFields: ['browser', 'module', 'main'], extensions: getPlatformsExtensions(env.platform), symlinks: false, }, // Turn off performance processing because we utilize // our own (CRA) hints via the FileSizeReporter // TODO: Bacon: Remove this higher value performance: defaults_1.isCI ? false : { maxAssetSize: 600000, maxEntrypointSize: 600000 }, }; if (isProd) { webpackConfig = (0, addons_1.withOptimizations)(webpackConfig); } else { webpackConfig = (0, addons_1.withDevServer)(webpackConfig, env, { allowedHost: argv.allowedHost, proxy: argv.proxy, }); } webpackConfig = (0, addons_1.withAlias)(webpackConfig, (0, env_1.getAliases)(env.projectRoot)); return webpackConfig; } exports.default = default_1; //# sourceMappingURL=webpack.config.js.map