"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.restartAsync = restartAsync; exports.printConnectionInstructions = printConnectionInstructions; exports.startAsync = startAsync; exports.stopAsync = stopAsync; exports.openAsync = openAsync; exports.compileWebAppAsync = compileWebAppAsync; exports.bundleWebAppAsync = bundleWebAppAsync; exports.bundleAsync = bundleAsync; exports.getProjectNameAsync = getProjectNameAsync; exports.getProjectUseNextJsAsync = getProjectUseNextJsAsync; exports.getServer = getServer; exports.getPort = getPort; exports.getUrlAsync = getUrlAsync; exports.getProtocolAsync = getProtocolAsync; exports.getAvailablePortAsync = getAvailablePortAsync; exports.setMode = setMode; exports.DEFAULT_PORT = exports.HOST = void 0; function ConfigUtils() { const data = _interopRequireWildcard(require("@expo/config")); ConfigUtils = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = function () { return data; }; return data; } function _express() { const data = _interopRequireDefault(require("express")); _express = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _getenv() { const data = _interopRequireDefault(require("getenv")); _getenv = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _formatWebpackMessages() { const data = _interopRequireDefault(require("react-dev-utils/formatWebpackMessages")); _formatWebpackMessages = function () { return data; }; return data; } function _WebpackDevServerUtils() { const data = require("react-dev-utils/WebpackDevServerUtils"); _WebpackDevServerUtils = function () { return data; }; return data; } function _webpack() { const data = _interopRequireDefault(require("webpack")); _webpack = function () { return data; }; return data; } function _webpackDevServer() { const data = _interopRequireDefault(require("webpack-dev-server")); _webpackDevServer = function () { return data; }; return data; } function _createWebpackCompiler() { const data = _interopRequireWildcard(require("./createWebpackCompiler")); _createWebpackCompiler = function () { return data; }; return data; } function _ip() { const data = _interopRequireDefault(require("./ip")); _ip = function () { return data; }; return data; } function ProjectUtils() { const data = _interopRequireWildcard(require("./project/ProjectUtils")); ProjectUtils = function () { return data; }; return data; } function ProjectSettings() { const data = _interopRequireWildcard(require("./ProjectSettings")); ProjectSettings = function () { return data; }; return data; } function Web() { const data = _interopRequireWildcard(require("./Web")); Web = function () { return data; }; return data; } function _XDLError() { const data = _interopRequireDefault(require("./XDLError")); _XDLError = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } // @ts-ignore missing types for Doctor until it gets converted to TypeScript const HOST = _getenv().default.string('WEB_HOST', '0.0.0.0'); exports.HOST = HOST; const DEFAULT_PORT = _getenv().default.int('WEB_PORT', 19006); exports.DEFAULT_PORT = DEFAULT_PORT; const WEBPACK_LOG_TAG = 'expo'; let webpackDevServerInstance = null; let webpackServerPort = null; async function restartAsync(projectRoot, options = {}) { await stopAsync(projectRoot); return await startAsync(projectRoot, options); } const PLATFORM_TAG = ProjectUtils().getPlatformTag('web'); const withTag = (...messages) => [PLATFORM_TAG + ' ', ...messages].join(''); let devServerInfo = null; function printConnectionInstructions(projectRoot, options = {}) { if (!devServerInfo) return; (0, _createWebpackCompiler().printInstructions)(projectRoot, { appName: devServerInfo.appName, urls: devServerInfo.urls, showInDevtools: false, showHelp: false, ...options }); } async function startAsync(projectRoot, options = {}, deprecatedVerbose) { if (typeof deprecatedVerbose !== 'undefined') { throw new (_XDLError().default)('WEBPACK_DEPRECATED', 'startAsync(root, options, verbose): The `verbose` option is deprecated.'); } const usingNextJs = await getProjectUseNextJsAsync(projectRoot); options.unimodulesOnly = usingNextJs; let serverName = 'Webpack'; if (usingNextJs) { serverName = 'Next.js'; } if (webpackDevServerInstance) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, withTag(_chalk().default.red(`${serverName} is already running.`))); return null; } const { env, config } = await createWebpackConfigAsync(projectRoot, options); const port = await getAvailablePortAsync({ defaultPort: options.port }); webpackServerPort = port; ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, withTag(`Starting ${serverName} on port ${webpackServerPort} in ${_chalk().default.underline(env.mode)} mode.`)); const protocol = env.https ? 'https' : 'http'; const urls = (0, _WebpackDevServerUtils().prepareUrls)(protocol, '::', webpackServerPort); const useYarn = ConfigUtils().isUsingYarn(projectRoot); const appName = await getProjectNameAsync(projectRoot); const nonInteractive = validateBoolOption('nonInteractive', options.nonInteractive, !process.stdout.isTTY); let server; if (usingNextJs) { if (protocol === 'https') { // TODO: Support https. throw new Error('https with Next.js is not supported for now.'); } server = await startNextJsAsync({ projectRoot, port: webpackServerPort, dev: env.mode !== 'production' }); (0, _createWebpackCompiler().printSuccessMessages)({ projectRoot, appName, urls, config, isFirstCompile: true, nonInteractive }); } else { devServerInfo = { urls, protocol, useYarn, appName, nonInteractive, port: webpackServerPort }; server = await new Promise(resolve => { // Create a webpack compiler that is configured with custom messages. const compiler = (0, _createWebpackCompiler().default)({ projectRoot, appName, config, urls, nonInteractive, webpackFactory: _webpack().default, onFinished: () => resolve(server) }); const server = new (_webpackDevServer().default)(compiler, config.devServer); // Launch WebpackDevServer. server.listen(port, HOST, error => { if (error) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, error.message); } if (typeof options.onWebpackFinished === 'function') { options.onWebpackFinished(error); } }); webpackDevServerInstance = server; }); } await ProjectSettings().setPackagerInfoAsync(projectRoot, { webpackServerPort }); const host = _ip().default.address(); const url = `${protocol}://${host}:${webpackServerPort}`; return { url, server, port, protocol, host }; } async function stopAsync(projectRoot) { if (webpackDevServerInstance) { ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, '\u203A Closing Webpack server'); webpackDevServerInstance.close(); webpackDevServerInstance = null; devServerInfo = null; webpackServerPort = null; await ProjectSettings().setPackagerInfoAsync(projectRoot, { webpackServerPort: null }); } } async function openAsync(projectRoot, options) { if (!webpackDevServerInstance) { await startAsync(projectRoot, options); } await Web().openProjectAsync(projectRoot); } async function compileWebAppAsync(projectRoot, compiler) { // We generate the stats.json file in the webpack-config const { warnings } = await new Promise((resolve, reject) => compiler.run((error, stats) => { let messages; if (error) { if (!error.message) { return reject(error); } messages = (0, _formatWebpackMessages().default)({ errors: [error.message], warnings: [], _showErrors: true, _showWarnings: true }); } else { messages = (0, _formatWebpackMessages().default)(stats.toJson({ all: false, warnings: true, errors: true })); } if (messages.errors.length) { // Only keep the first error. Others are often indicative // of the same problem, but confuse the reader with noise. if (messages.errors.length > 1) { messages.errors.length = 1; } return reject(new Error(messages.errors.join('\n\n'))); } if (process.env.CI && (typeof process.env.CI !== 'string' || process.env.CI.toLowerCase() !== 'false') && messages.warnings.length) { ProjectUtils().logWarning(projectRoot, WEBPACK_LOG_TAG, withTag(_chalk().default.yellow('\nTreating warnings as errors because process.env.CI = true.\n' + 'Most CI servers set it automatically.\n'))); return reject(new Error(messages.warnings.join('\n\n'))); } resolve({ warnings: messages.warnings }); })); return { warnings }; } async function bundleWebAppAsync(projectRoot, config) { const compiler = (0, _webpack().default)(config); try { const { warnings } = await compileWebAppAsync(projectRoot, compiler); if (warnings.length) { ProjectUtils().logWarning(projectRoot, WEBPACK_LOG_TAG, withTag(_chalk().default.yellow('Compiled with warnings.\n'))); ProjectUtils().logWarning(projectRoot, WEBPACK_LOG_TAG, warnings.join('\n\n')); } else { ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, withTag(_chalk().default.green('Compiled successfully.\n'))); } } catch (error) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, withTag(_chalk().default.red('Failed to compile.\n'))); throw error; } } async function bundleAsync(projectRoot, options) { const isUsingNextJs = await getProjectUseNextJsAsync(projectRoot); const { config } = await createWebpackConfigAsync(projectRoot, { ...options, unimodulesOnly: isUsingNextJs }); if (isUsingNextJs) { await bundleNextJsAsync(projectRoot); } else { await bundleWebAppAsync(projectRoot, config); } } async function getProjectNameAsync(projectRoot) { const { exp } = await ConfigUtils().readConfigJsonAsync(projectRoot, true); const { webName } = ConfigUtils().getNameFromConfig(exp); return webName; } async function getProjectUseNextJsAsync(projectRoot) { const { exp } = await ConfigUtils().readConfigJsonAsync(projectRoot, true); const { use = null } = exp.web || {}; return use === 'nextjs'; } function getServer(projectRoot) { if (webpackDevServerInstance == null) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, withTag('Webpack is not running.')); } return webpackDevServerInstance; } function getPort() { return webpackServerPort; } async function getUrlAsync(projectRoot) { const devServer = getServer(projectRoot); if (!devServer) { return null; } const host = _ip().default.address(); const protocol = await getProtocolAsync(projectRoot); return `${protocol}://${host}:${webpackServerPort}`; } async function getProtocolAsync(projectRoot) { // TODO: Bacon: Handle when not in expo const { https } = await ProjectSettings().readAsync(projectRoot); return https === true ? 'https' : 'http'; } async function getAvailablePortAsync(options = {}) { try { const defaultPort = 'defaultPort' in options && options.defaultPort ? options.defaultPort : DEFAULT_PORT; const port = await (0, _WebpackDevServerUtils().choosePort)('host' in options && options.host ? options.host : HOST, defaultPort); if (!port) throw new Error(`Port ${defaultPort} not available.`);else return port; } catch (error) { throw new (_XDLError().default)('NO_PORT_FOUND', 'No available port found: ' + error.message); } } function setMode(mode) { process.env.BABEL_ENV = mode; process.env.NODE_ENV = mode; } function validateBoolOption(name, value, defaultValue) { if (typeof value === 'undefined') { value = defaultValue; } if (typeof value !== 'boolean') { throw new (_XDLError().default)('WEBPACK_INVALID_OPTION', `'${name}' option must be a boolean.`); } return value; } function transformCLIOptions(options) { // Transform the CLI flags into more explicit values return { ...options, isImageEditingEnabled: options.pwa }; } async function createWebpackConfigAsync(projectRoot, options = {}) { const fullOptions = transformCLIOptions(options); const env = await getWebpackConfigEnvFromBundlingOptionsAsync(projectRoot, fullOptions); setMode(env.mode); let config; if (options.unimodulesOnly) { const withUnimodules = require('@expo/webpack-config/withUnimodules'); config = withUnimodules({}, env); } else { config = await Web().invokeWebpackConfigAsync(env); } return { env, config }; } async function applyOptionsToProjectSettingsAsync(projectRoot, options) { let newSettings = {}; // Change settings before reading them if (typeof options.https === 'boolean') { newSettings.https = options.https; } if (typeof options.dev === 'boolean') { newSettings.dev = options.dev; } if (Object.keys(newSettings).length) { await ProjectSettings().setAsync(projectRoot, newSettings); } return await ProjectSettings().readAsync(projectRoot); } async function getWebpackConfigEnvFromBundlingOptionsAsync(projectRoot, options) { // Bacon: Prevent dev flag from being used in production if (options.mode === 'production') { options.dev = false; } let { dev, https } = await applyOptionsToProjectSettingsAsync(projectRoot, options); const mode = typeof options.mode === 'string' ? options.mode : dev ? 'development' : 'production'; const isImageEditingEnabled = validateBoolOption('isImageEditingEnabled', options.isImageEditingEnabled, true); const isDebugInfoEnabled = validateBoolOption('isDebugInfoEnabled', options.isDebugInfoEnabled, Web().isInfoEnabled()); return { projectRoot, pwa: isImageEditingEnabled, mode, https, info: isDebugInfoEnabled, ...(options.webpackEnv || {}) }; } async function startNextJsAsync({ projectRoot, port, dev }) { const { exp } = await ConfigUtils().readConfigJsonAsync(projectRoot, true); let next; try { next = require(ConfigUtils().resolveModule('next', projectRoot, exp)); } catch (_unused) { throw new (_XDLError().default)('NEXTJS_NOT_INSTALLED', 'Next.js is not installed in your app. See https://docs.expo.io/versions/latest/guides/using-nextjs/'); } // Build first if in production mode. // https://nextjs.org/docs#custom-server-and-routing if (!dev) { await bundleNextJsAsync(projectRoot); } await _copyCustomNextJsTemplatesAsync(projectRoot); const app = next({ dev, dir: projectRoot }); const handle = app.getRequestHandler(); await app.prepare(); const server = (0, _express().default)(); server.get('/expo-service-worker.js', (req, res) => { res.sendFile(_path().default.resolve(projectRoot, 'static', 'expo-service-worker.js')); }); server.get('/service-worker.js', (req, res) => { // This file should be provided by https://github.com/hanford/next-offline if installed. const serviceWorkerPath = _path().default.resolve(projectRoot, '.next', 'service-worker.js'); if (!_fsExtra().default.existsSync(serviceWorkerPath)) { // Simply return a blank service worker file if the user is not using `next-offline`. res.sendFile(_path().default.resolve(projectRoot, 'static', 'service-worker.js')); return; } res.sendFile(serviceWorkerPath); }); server.get('*', handle); webpackDevServerInstance = server.listen(port, err => { if (err) { throw new Error(`Express server failed to start: ${err.toString()}`); } }); return webpackDevServerInstance; } async function bundleNextJsAsync(projectRoot) { const { exp } = await ConfigUtils().readConfigJsonAsync(projectRoot, true); let nextBuild; try { nextBuild = require(ConfigUtils().resolveModule('next/dist/build', projectRoot, exp)).default; } catch (_unused2) { throw new (_XDLError().default)('NEXTJS_NOT_INSTALLED', 'Next.js (or its build component) is not installed in your app. See https://docs.expo.io/versions/latest/guides/using-nextjs/'); } await _copyCustomNextJsTemplatesAsync(projectRoot); await nextBuild(projectRoot); } async function _copyCustomNextJsTemplatesAsync(projectRoot) { try { await _fsExtra().default.writeFile(_path().default.join(projectRoot, '.expo', 'next_document.js'), nextJsDocument); } catch (e) { throw new Error(`Could not write to _document.js: ${e.toString()}`); } const pagesDocument = _path().default.join(projectRoot, 'pages', '_document.js'); if (!_fsExtra().default.existsSync(pagesDocument)) { // Only write to `pages/_document.js` if it doesn't exists. try { await _fsExtra().default.writeFile(pagesDocument, nextJsImportDocument); } catch (e) { throw new Error(`Could not write to pages/_document.js: ${e.toString()}`); } } // TODO: Use `public/` folder when Next.js eventually deprecates `static/` folder. const staticFolder = _path().default.join(projectRoot, 'static'); if (!_fsExtra().default.existsSync(staticFolder)) { _fsExtra().default.mkdirSync(staticFolder); } try { await _fsExtra().default.copyFile(require.resolve('@expo/webpack-config/web-default/expo-service-worker.js'), _path().default.join(staticFolder, 'expo-service-worker.js')); } catch (e) { throw new Error(`Could not copy expo-service-worker.js: ${e.toString()}`); } const serviceWorkerPath = _path().default.join(staticFolder, 'service-worker.js'); if (!_fsExtra().default.existsSync(serviceWorkerPath)) { // Write a blank service-worker.js file for users who do not use any other service worker. try { await _fsExtra().default.writeFile(serviceWorkerPath, ''); } catch (e) { throw new Error(`Could not write to service-worker.js: ${e.toString()}`); } } } const nextJsDocument = `\ // Based on https://github.com/zeit/next.js/tree/canary/examples/with-react-native-web // and https://github.com/expo/expo-cli/blob/master/packages/webpack-config/web-default/index.html import Document, { Head, Main, NextScript } from 'next/document' import React from 'react' import { AppRegistry } from 'react-native' const normalizeNextElements = \` /** * Building on the RNWeb reset: * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js */ html, body, #__next { width: 100%; /* To smooth any scrolling behavior */ -webkit-overflow-scrolling: touch; margin: 0px; padding: 0px; /* Allows content to fill the viewport and go beyond the bottom */ min-height: 100%; } #__next { flex-shrink: 0; flex-basis: auto; flex-grow: 1; display: flex; flex: 1; } html { font-size: 14px; scroll-behavior: smooth; /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */ -webkit-text-size-adjust: 100%; height: 100%; } body { display: flex; /* Allows you to scroll below the viewport; default value is visible */ overflow-y: auto; overscroll-behavior-y: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -ms-overflow-style: scrollbar; } \`; export default class ExpoDocument extends Document { static async getInitialProps ({ renderPage }) { AppRegistry.registerComponent('Main', () => Main) const { getStyleElement } = AppRegistry.getApplication('Main') const page = renderPage() const styles = [ <style dangerouslySetInnerHTML={{ __html: normalizeNextElements }} />, getStyleElement() ] return { ...page, styles: React.Children.toArray(styles) } } render () { return ( <html> <Head> <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> </Head> <body> <Main /> <NextScript /> </body> </html> ); } } `; const nextJsImportDocument = `\ import ExpoDocument from '../.expo/next_document'; export default ExpoDocument; `; //# sourceMappingURL=__sourcemaps__/Webpack.js.map