UNPKG

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