"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