UNPKG

20.7 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5var __importStar = (this && this.__importStar) || function (mod) {
6 if (mod && mod.__esModule) return mod;
7 var result = {};
8 if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
9 result["default"] = mod;
10 return result;
11};
12Object.defineProperty(exports, "__esModule", { value: true });
13/** @internal */ /** */
14/* eslint-env node */
15const config_1 = require("@expo/config");
16const react_refresh_webpack_plugin_1 = __importDefault(require("@pmmmwh/react-refresh-webpack-plugin"));
17const chalk_1 = __importDefault(require("chalk"));
18const clean_webpack_plugin_1 = require("clean-webpack-plugin");
19const copy_webpack_plugin_1 = __importDefault(require("copy-webpack-plugin"));
20const expo_pwa_1 = require("expo-pwa");
21const fs_extra_1 = require("fs-extra");
22const getenv_1 = require("getenv");
23const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plugin"));
24const node_html_parser_1 = require("node-html-parser");
25const path_1 = __importDefault(require("path"));
26const pnp_webpack_plugin_1 = __importDefault(require("pnp-webpack-plugin"));
27const ModuleNotFoundPlugin_1 = __importDefault(require("react-dev-utils/ModuleNotFoundPlugin"));
28const WatchMissingNodeModulesPlugin_1 = __importDefault(require("react-dev-utils/WatchMissingNodeModulesPlugin"));
29const webpack_1 = __importStar(require("webpack"));
30const webpack_deep_scope_plugin_1 = __importDefault(require("webpack-deep-scope-plugin"));
31const webpack_manifest_plugin_1 = __importDefault(require("webpack-manifest-plugin"));
32const addons_1 = require("./addons");
33const env_1 = require("./env");
34const loaders_1 = require("./loaders");
35const plugins_1 = require("./plugins");
36const ExpoAppManifestWebpackPlugin_1 = __importDefault(require("./plugins/ExpoAppManifestWebpackPlugin"));
37const utils_1 = require("./utils");
38// Source maps are resource heavy and can cause out of memory issue for large source files.
39const shouldUseSourceMap = getenv_1.boolish('GENERATE_SOURCEMAP', true);
40const shouldUseNativeCodeLoading = getenv_1.boolish('EXPO_WEBPACK_USE_NATIVE_CODE_LOADING', true);
41const shouldUseReactRefresh = getenv_1.boolish('EXPO_WEBPACK_FAST_REFRESH', false);
42const isCI = getenv_1.boolish('CI', false);
43function getDevtool({ production, development }, { devtool }) {
44 if (production) {
45 // string or false
46 if (devtool !== undefined) {
47 // When big assets are involved sources maps can become expensive and cause your process to run out of memory.
48 return devtool;
49 }
50 return shouldUseSourceMap ? 'source-map' : false;
51 }
52 if (development) {
53 return 'cheap-module-source-map';
54 }
55 return false;
56}
57function getOutput(locations, mode, publicPath, platform) {
58 const commonOutput = {
59 // We inferred the "public path" (such as / or /my-project) from homepage.
60 // We use "/" in development.
61 publicPath,
62 // Build folder (default `web-build`)
63 path: locations.production.folder,
64 // this defaults to 'window', but by setting it to 'this' then
65 // module chunks which are built will work in web workers as well.
66 globalObject: 'this',
67 };
68 if (mode === 'production') {
69 commonOutput.filename = 'static/js/[name].[contenthash:8].js';
70 // There are also additional JS chunk files if you use code splitting.
71 commonOutput.chunkFilename = 'static/js/[name].[contenthash:8].chunk.js';
72 // Point sourcemap entries to original disk location (format as URL on Windows)
73 commonOutput.devtoolModuleFilenameTemplate = (info) => path_1.default.relative(locations.root, info.absoluteResourcePath).replace(/\\/g, '/');
74 }
75 else {
76 // Add comments that describe the file import/exports.
77 // This will make it easier to debug.
78 commonOutput.pathinfo = true;
79 // Give the output bundle a constant name to prevent caching.
80 // Also there are no actual files generated in dev.
81 commonOutput.filename = 'static/js/bundle.js';
82 // There are also additional JS chunk files if you use code splitting.
83 commonOutput.chunkFilename = 'static/js/[name].chunk.js';
84 // Point sourcemap entries to original disk location (format as URL on Windows)
85 commonOutput.devtoolModuleFilenameTemplate = (info) => path_1.default.resolve(info.absoluteResourcePath).replace(/\\/g, '/');
86 }
87 if (!shouldUseNativeCodeLoading && isPlatformNative(platform)) {
88 // Give the output bundle a constant name to prevent caching.
89 // Also there are no actual files generated in dev.
90 commonOutput.filename = `index.bundle`;
91 }
92 return commonOutput;
93}
94function isPlatformNative(platform) {
95 return ['ios', 'android'].includes(platform);
96}
97function getPlatformsExtensions(platform) {
98 if (isPlatformNative(platform)) {
99 return env_1.getNativeModuleFileExtensions(platform, 'native');
100 }
101 return env_1.getModuleFileExtensions(platform);
102}
103async function default_1(env, argv = {}) {
104 var _a, _b, _c, _d, _e, _f, _g;
105 if ('report' in env) {
106 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`));
107 }
108 const config = env_1.getConfig(env);
109 const mode = env_1.getMode(env);
110 const isDev = mode === 'development';
111 const isProd = mode === 'production';
112 // Because the native React runtime is different to the browser we need to make
113 // some core modifications to webpack.
114 const isNative = ['ios', 'android'].includes(env.platform);
115 // Enables deep scope analysis in production mode.
116 // Remove unused import/exports
117 // override: `env.removeUnusedImportExports`
118 const deepScopeAnalysisEnabled = utils_1.overrideWithPropertyOrConfig(env.removeUnusedImportExports, false
119 // isProd
120 );
121 const locations = env.locations || (await env_1.getPathsAsync(env.projectRoot));
122 const { publicPath, publicUrl } = env_1.getPublicPaths(env);
123 const { build: buildConfig = {} } = config.web || {};
124 const devtool = getDevtool({ production: isProd, development: isDev }, buildConfig);
125 const appEntry = [];
126 // In solutions like Gatsby the main entry point doesn't need to be known.
127 if (locations.appMain) {
128 appEntry.push(locations.appMain);
129 }
130 const webpackDevClientEntry = require.resolve('react-dev-utils/webpackHotDevClient');
131 if (isNative) {
132 const reactNativeModulePath = config_1.projectHasModule('react-native', env.projectRoot, (_a = env.config) !== null && _a !== void 0 ? _a : {});
133 if (reactNativeModulePath) {
134 for (const polyfill of [
135 'Core/InitializeCore.js',
136 'polyfills/Object.es7.js',
137 'polyfills/error-guard.js',
138 'polyfills/console.js',
139 ]) {
140 const resolvedPolyfill = config_1.projectHasModule(`react-native/Libraries/${polyfill}`, env.projectRoot, (_b = env.config) !== null && _b !== void 0 ? _b : {});
141 if (resolvedPolyfill)
142 appEntry.unshift(resolvedPolyfill);
143 }
144 }
145 }
146 else {
147 // Add a loose requirement on the ResizeObserver polyfill if it's installed...
148 // Avoid `withEntry` as we don't need so much complexity with this config.
149 const resizeObserverPolyfill = config_1.projectHasModule('resize-observer-polyfill/dist/ResizeObserver.global', env.projectRoot, config);
150 if (resizeObserverPolyfill) {
151 appEntry.unshift(resizeObserverPolyfill);
152 }
153 if (isDev) {
154 // https://github.com/facebook/create-react-app/blob/e59e0920f3bef0c2ac47bbf6b4ff3092c8ff08fb/packages/react-scripts/config/webpack.config.js#L144
155 // Include an alternative client for WebpackDevServer. A client's job is to
156 // connect to WebpackDevServer by a socket and get notified about changes.
157 // When you save a file, the client will either apply hot updates (in case
158 // of CSS changes), or refresh the page (in case of JS changes). When you
159 // make a syntax error, this client will display a syntax error overlay.
160 // Note: instead of the default WebpackDevServer client, we use a custom one
161 // to bring better experience for Create React App users. You can replace
162 // the line below with these two lines if you prefer the stock client:
163 // require.resolve('webpack-dev-server/client') + '?/',
164 // require.resolve('webpack/hot/dev-server'),
165 appEntry.unshift(webpackDevClientEntry);
166 }
167 }
168 let generatePWAImageAssets = !isNative && !isDev;
169 if (!isDev && typeof env.pwa !== 'undefined') {
170 generatePWAImageAssets = env.pwa;
171 }
172 const filesToCopy = [
173 {
174 from: locations.template.folder,
175 to: locations.production.folder,
176 toType: 'dir',
177 globOptions: {
178 dot: true,
179 // We generate new versions of these based on the templates
180 ignore: [
181 '**/expo-service-worker.*',
182 // '**/serve.json',
183 // '**/index.html',
184 '**/icon.png',
185 // We copy this over in `withWorkbox` as it must be part of the Webpack `entry` and have templates replaced.
186 '**/register-service-worker.js',
187 ],
188 },
189 },
190 {
191 from: locations.template.serveJson,
192 to: locations.production.serveJson,
193 },
194 ];
195 if (env.offline === true) {
196 filesToCopy.push({
197 from: locations.template.serviceWorker,
198 to: locations.production.serviceWorker,
199 });
200 }
201 const templateIndex = node_html_parser_1.parse(fs_extra_1.readFileSync(locations.template.indexHtml, { encoding: 'utf8' }));
202 // @ts-ignore
203 const templateLinks = templateIndex.querySelectorAll('link');
204 const links = templateLinks.map((value) => ({
205 rel: value.getAttribute('rel'),
206 media: value.getAttribute('media'),
207 href: value.getAttribute('href'),
208 sizes: value.getAttribute('sizes'),
209 node: value,
210 }));
211 // @ts-ignore
212 const templateMeta = templateIndex.querySelectorAll('meta');
213 const meta = templateMeta.map((value) => ({
214 name: value.getAttribute('name'),
215 content: value.getAttribute('content'),
216 node: value,
217 }));
218 const [manifestLink] = links.filter(link => typeof link.rel === 'string' && link.rel.split(' ').includes('manifest'));
219 let templateManifest = locations.template.manifest;
220 if (manifestLink && manifestLink.href) {
221 templateManifest = locations.template.get(manifestLink.href);
222 }
223 const ensureSourceAbsolute = (input) => {
224 if (!input)
225 return input;
226 return Object.assign(Object.assign({}, input), { src: locations.absolute(input.src) });
227 };
228 // TODO(Bacon): Move to expo/config - manifest code from XDL Project
229 const publicConfig = Object.assign(Object.assign({}, config), { xde: true, developer: {
230 tool: 'expo-cli',
231 projectRoot: env.projectRoot,
232 }, packagerOpts: {
233 dev: !isProd,
234 minify: isProd,
235 https: env.https,
236 } });
237 let webpackConfig = {
238 mode,
239 entry: {
240 app: appEntry,
241 },
242 // https://webpack.js.org/configuration/other-options/#bail
243 // Fail out on the first error instead of tolerating it.
244 bail: isProd,
245 devtool,
246 // TODO(Bacon): Simplify this while ensuring gatsby support continues to work.
247 context: isNative ? (_c = env.projectRoot) !== null && _c !== void 0 ? _c : __dirname : __dirname,
248 // configures where the build ends up
249 output: getOutput(locations, mode, publicPath, env.platform),
250 plugins: [
251 // Delete the build folder
252 isProd &&
253 new clean_webpack_plugin_1.CleanWebpackPlugin({
254 cleanOnceBeforeBuildPatterns: [locations.production.folder],
255 dry: false,
256 verbose: false,
257 }),
258 // Copy the template files over
259 isProd && new copy_webpack_plugin_1.default({ patterns: filesToCopy }),
260 // Generate the `index.html`
261 new plugins_1.ExpoHtmlWebpackPlugin(env, templateIndex),
262 plugins_1.ExpoInterpolateHtmlPlugin.fromEnv(env, plugins_1.ExpoHtmlWebpackPlugin),
263 isNative &&
264 new ExpoAppManifestWebpackPlugin_1.default({
265 template: locations.template.get('app.config.json'),
266 path: 'app.config.json',
267 publicPath,
268 }, publicConfig),
269 env.pwa &&
270 new plugins_1.ExpoPwaManifestWebpackPlugin({
271 template: templateManifest,
272 path: 'manifest.json',
273 publicPath,
274 }, config),
275 !isNative &&
276 new plugins_1.FaviconWebpackPlugin({
277 projectRoot: env.projectRoot,
278 publicPath,
279 links,
280 }, ensureSourceAbsolute(expo_pwa_1.getFaviconIconConfig(config))),
281 generatePWAImageAssets &&
282 new plugins_1.ApplePwaWebpackPlugin({
283 projectRoot: env.projectRoot,
284 publicPath,
285 links,
286 meta,
287 }, {
288 name: (_d = env.config.web) === null || _d === void 0 ? void 0 : _d.shortName,
289 isFullScreen: (_e = env.config.web) === null || _e === void 0 ? void 0 : _e.meta.apple.touchFullscreen,
290 isWebAppCapable: (_f = env.config.web) === null || _f === void 0 ? void 0 : _f.meta.apple.mobileWebAppCapable,
291 barStyle: (_g = env.config.web) === null || _g === void 0 ? void 0 : _g.meta.apple.barStyle,
292 }, ensureSourceAbsolute(expo_pwa_1.getSafariIconConfig(env.config)), ensureSourceAbsolute(expo_pwa_1.getSafariStartupImageConfig(env.config))),
293 generatePWAImageAssets &&
294 new plugins_1.ChromeIconsWebpackPlugin({
295 projectRoot: env.projectRoot,
296 publicPath,
297 }, ensureSourceAbsolute(expo_pwa_1.getChromeIconConfig(config))),
298 // This gives some necessary context to module not found errors, such as
299 // the requesting resource.
300 new ModuleNotFoundPlugin_1.default(locations.root),
301 new plugins_1.ExpoDefinePlugin({
302 mode,
303 publicUrl,
304 config,
305 }),
306 // Disable chunking on native
307 // https://gist.github.com/glennreyes/f538a369db0c449b681e86ef7f86a254#file-razzle-config-js
308 isNative &&
309 new webpack_1.default.optimize.LimitChunkCountPlugin({
310 maxChunks: 1,
311 }),
312 // This is necessary to emit hot updates (currently CSS only):
313 !isNative && isDev && new webpack_1.HotModuleReplacementPlugin(),
314 // Experimental hot reloading for React .
315 // https://github.com/facebook/react/tree/master/packages/react-refresh
316 isDev &&
317 shouldUseReactRefresh &&
318 new react_refresh_webpack_plugin_1.default({
319 overlay: {
320 entry: webpackDevClientEntry,
321 },
322 }),
323 // If you require a missing module and then `npm install` it, you still have
324 // to restart the development server for Webpack to discover it. This plugin
325 // makes the discovery automatic so you don't have to restart.
326 // See https://github.com/facebook/create-react-app/issues/186
327 isDev && new WatchMissingNodeModulesPlugin_1.default(locations.modules),
328 !isNative &&
329 isProd &&
330 new mini_css_extract_plugin_1.default({
331 // Options similar to the same options in webpackOptions.output
332 // both options are optional
333 filename: 'static/css/[name].[contenthash:8].css',
334 chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
335 }),
336 // Generate an asset manifest file with the following content:
337 // - "files" key: Mapping of all asset filenames to their corresponding
338 // output file so that tools can pick it up without having to parse
339 // `index.html`
340 // - "entrypoints" key: Array of files which are included in `index.html`,
341 // can be used to reconstruct the HTML if necessary
342 !isNative &&
343 new webpack_manifest_plugin_1.default({
344 fileName: 'asset-manifest.json',
345 publicPath,
346 filter: ({ path }) => {
347 if (path.match(/(apple-touch-startup-image|apple-touch-icon|chrome-icon|precache-manifest)/)) {
348 return false;
349 }
350 // Remove compressed versions and service workers
351 return !(path.endsWith('.gz') || path.endsWith('worker.js'));
352 },
353 generate: (seed, files, entrypoints) => {
354 const manifestFiles = files.reduce((manifest, file) => {
355 if (file.name) {
356 manifest[file.name] = file.path;
357 }
358 return manifest;
359 }, seed);
360 const entrypointFiles = entrypoints.app.filter(fileName => !fileName.endsWith('.map'));
361 return {
362 files: manifestFiles,
363 entrypoints: entrypointFiles,
364 };
365 },
366 }),
367 deepScopeAnalysisEnabled && new webpack_deep_scope_plugin_1.default(),
368 // Skip using a progress bar in CI
369 !isCI && new plugins_1.ExpoProgressBarPlugin(),
370 ].filter(Boolean),
371 module: {
372 strictExportPresence: false,
373 rules: [
374 // Disable require.ensure because it breaks tree shaking.
375 { parser: { requireEnsure: false } },
376 {
377 oneOf: loaders_1.createAllLoaders(env),
378 },
379 ].filter(Boolean),
380 },
381 resolveLoader: {
382 plugins: [
383 // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
384 // from the current package.
385 pnp_webpack_plugin_1.default.moduleLoader(module),
386 ],
387 },
388 resolve: {
389 extensions: getPlatformsExtensions(env.platform),
390 plugins: [
391 // Adds support for installing with Plug'n'Play, leading to faster installs and adding
392 // guards against forgotten dependencies and such.
393 pnp_webpack_plugin_1.default,
394 ],
395 symlinks: false,
396 },
397 // Turn off performance processing because we utilize
398 // our own (CRA) hints via the FileSizeReporter
399 // TODO: Bacon: Remove this higher value
400 performance: isCI ? false : { maxAssetSize: 600000, maxEntrypointSize: 600000 },
401 };
402 if (!shouldUseNativeCodeLoading) {
403 webpackConfig = addons_1.withPlatformSourceMaps(webpackConfig, env);
404 }
405 if (isNative) {
406 // https://github.com/webpack/webpack/blob/f06086c53b2277e421604c5cea6f32f5c5b6d117/declarations/WebpackOptions.d.ts#L504-L518
407 webpackConfig.target = 'webworker';
408 }
409 if (isProd) {
410 webpackConfig = addons_1.withOptimizations(webpackConfig);
411 }
412 else {
413 webpackConfig = addons_1.withDevServer(webpackConfig, env, {
414 allowedHost: argv.allowedHost,
415 proxy: argv.proxy,
416 });
417 }
418 if (!isNative) {
419 webpackConfig = addons_1.withNodeMocks(addons_1.withAlias(webpackConfig, env_1.getAliases(env.projectRoot)));
420 }
421 return webpackConfig;
422}
423exports.default = default_1;
424//# sourceMappingURL=webpack.config.js.map
\No newline at end of file