UNPKG

@expo/xdl

Version:
765 lines (604 loc) 21.3 kB
"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.isRunning = isRunning; exports.getServer = getServer; exports.getPort = getPort; exports.getUrlAsync = getUrlAsync; exports.invokeWebpackConfigAsync = invokeWebpackConfigAsync; exports.openProjectAsync = openProjectAsync; function _config() { const data = require("@expo/config"); _config = function () { return data; }; return data; } function devcert() { const data = _interopRequireWildcard(require("@expo/devcert")); devcert = function () { return data; }; return data; } function _packageManager() { const data = require("@expo/package-manager"); _packageManager = function () { return data; }; return data; } function _chalk() { const data = _interopRequireDefault(require("chalk")); _chalk = 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 = _interopRequireWildcard(require("path")); path = function () { return data; }; return data; } function _WebpackDevServerUtils() { const data = require("react-dev-utils/WebpackDevServerUtils"); _WebpackDevServerUtils = function () { return data; }; return data; } function _formatWebpackMessages() { const data = _interopRequireDefault(require("react-dev-utils/formatWebpackMessages")); _formatWebpackMessages = function () { return data; }; return data; } function _openBrowser() { const data = _interopRequireDefault(require("react-dev-utils/openBrowser")); _openBrowser = 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 _Logger() { const data = _interopRequireDefault(require("./Logger")); _Logger = function () { return data; }; return data; } function ProjectSettings() { const data = _interopRequireWildcard(require("./ProjectSettings")); ProjectSettings = function () { return data; }; return data; } function UrlUtils() { const data = _interopRequireWildcard(require("./UrlUtils")); UrlUtils = function () { return data; }; return data; } function Versions() { const data = _interopRequireWildcard(require("./Versions")); Versions = function () { return data; }; return data; } function _XDLError() { const data = _interopRequireDefault(require("./XDLError")); _XDLError = function () { return data; }; return data; } function _ip() { const data = _interopRequireDefault(require("./ip")); _ip = function () { return data; }; return data; } function _TerminalLink() { const data = require("./logs/TerminalLink"); _TerminalLink = function () { return data; }; return data; } function ProjectUtils() { const data = _interopRequireWildcard(require("./project/ProjectUtils")); ProjectUtils = function () { return data; }; return data; } function _WebpackEnvironment() { const data = require("./webpack-utils/WebpackEnvironment"); _WebpackEnvironment = function () { return data; }; return data; } function _createWebpackCompiler() { const data = _interopRequireWildcard(require("./webpack-utils/createWebpackCompiler")); _createWebpackCompiler = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const WEBPACK_LOG_TAG = 'expo'; let webpackDevServerInstance = null; let webpackServerPort = null; async function restartAsync(projectRoot, options = {}) { await stopAsync(projectRoot); return await startAsync(projectRoot, options); } let devServerInfo = null; function printConnectionInstructions(projectRoot, options = {}) { if (!devServerInfo) return; (0, _createWebpackCompiler().printInstructions)(projectRoot, { appName: devServerInfo.appName, urls: devServerInfo.urls, showInDevtools: false, ...options }); } async function clearWebCacheAsync(projectRoot, mode) { const cacheFolder = path().join(projectRoot, '.expo', 'web', 'cache', mode); ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, _chalk().default.dim(`Clearing ${mode} cache directory...`)); try { await _fsExtra().default.remove(cacheFolder); } catch (_unused) {} } 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 serverName = 'Webpack'; if (webpackDevServerInstance) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, _chalk().default.red(`${serverName} is already running.`)); return null; } const fullOptions = transformCLIOptions(options); const env = await getWebpackConfigEnvFromBundlingOptionsAsync(projectRoot, fullOptions); if (fullOptions.clear) { await clearWebCacheAsync(projectRoot, env.mode); } if (env.https) { if (!process.env.SSL_CRT_FILE || !process.env.SSL_KEY_FILE) { const ssl = await getSSLCertAsync({ name: 'localhost', directory: projectRoot }); if (ssl) { process.env.SSL_CRT_FILE = ssl.certPath; process.env.SSL_KEY_FILE = ssl.keyPath; } } } const config = await createWebpackConfigAsync(env, fullOptions); const port = await getAvailablePortAsync({ defaultPort: options.port }); webpackServerPort = port; ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, `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 = (0, _packageManager().isUsingYarn)(projectRoot); const appName = await getProjectNameAsync(projectRoot); const nonInteractive = validateBoolOption('nonInteractive', options.nonInteractive, !process.stdout.isTTY); devServerInfo = { urls, protocol, useYarn, appName, nonInteractive, port: webpackServerPort }; const 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, _WebpackEnvironment().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}:${port}`; return { url, server, port, protocol, host }; } async function stopAsync(projectRoot) { if (webpackDevServerInstance) { await new Promise(res => { if (webpackDevServerInstance) { ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, '\u203A Stopping Webpack server'); webpackDevServerInstance.close(res); } }); webpackDevServerInstance = null; devServerInfo = null; webpackServerPort = null; await ProjectSettings().setPackagerInfoAsync(projectRoot, { webpackServerPort: null }); } } async function openAsync(projectRoot, options) { if (!webpackDevServerInstance) { await startAsync(projectRoot, options); } await 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 (_getenv().default.boolish('EXPO_WEB_BUILD_STRICT', false) && _getenv().default.boolish('CI', false) && messages.warnings.length) { ProjectUtils().logWarning(projectRoot, WEBPACK_LOG_TAG, _chalk().default.yellow('\nTreating warnings as errors because `process.env.CI = true` and `process.env.EXPO_WEB_BUILD_STRICT = 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, _chalk().default.yellow('Compiled with warnings.\n')); ProjectUtils().logWarning(projectRoot, WEBPACK_LOG_TAG, warnings.join('\n\n')); } else { ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, _chalk().default.green('Compiled successfully.\n')); } } catch (error) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, _chalk().default.red('Failed to compile.\n')); throw error; } } async function bundleAsync(projectRoot, options) { var _config$plugins; const fullOptions = transformCLIOptions({ ...options }); const env = await getWebpackConfigEnvFromBundlingOptionsAsync(projectRoot, { ...fullOptions, // Force production mode: 'production' }); if (typeof env.offline === 'undefined') { try { const expoConfig = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); // If offline isn't defined, check the version and keep offline enabled for SDK 38 and prior if (expoConfig.exp.sdkVersion) if (Versions().lteSdkVersion(expoConfig.exp, '38.0.0')) { env.offline = true; } } catch (_unused2) {// Ignore the error thrown by projects without an Expo config. } } if (fullOptions.clear) { await clearWebCacheAsync(projectRoot, env.mode); } const config = await createWebpackConfigAsync(env, fullOptions); await bundleWebAppAsync(projectRoot, config); const hasSWPlugin = (_config$plugins = config.plugins) === null || _config$plugins === void 0 ? void 0 : _config$plugins.find(item => { var _item$constructor; return (item === null || item === void 0 ? void 0 : (_item$constructor = item.constructor) === null || _item$constructor === void 0 ? void 0 : _item$constructor.name) === 'GenerateSW'; }); if (!hasSWPlugin) { ProjectUtils().logInfo(projectRoot, WEBPACK_LOG_TAG, _chalk().default.green(`Offline (PWA) support is not enabled in this build. ${_chalk().default.dim((0, _TerminalLink().learnMore)('https://expo.fyi/enabling-web-service-workers'))}\n`)); } } async function getProjectNameAsync(projectRoot) { var _getNameFromConfig$we; const { exp } = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); const webName = (_getNameFromConfig$we = (0, _config().getNameFromConfig)(exp).webName) !== null && _getNameFromConfig$we !== void 0 ? _getNameFromConfig$we : exp.name; return webName; } function isRunning() { return !!webpackDevServerInstance; } function getServer(projectRoot) { if (webpackDevServerInstance == null) { ProjectUtils().logError(projectRoot, WEBPACK_LOG_TAG, 'Webpack is not running.'); } return webpackDevServerInstance; } function getPort() { return webpackServerPort; } /** * Get the URL for the running instance of Webpack dev server. * * @param projectRoot */ 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 : _WebpackEnvironment().DEFAULT_PORT; const port = await (0, _WebpackDevServerUtils().choosePort)('host' in options && options.host ? options.host : _WebpackEnvironment().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(env, options = {}) { setMode(env.mode); let config; if (options.unimodulesOnly) { const { withUnimodules } = require('@expo/webpack-config/addons'); config = withUnimodules({}, env); } else { config = await invokeWebpackConfigAsync(env); } return config; } async function applyOptionsToProjectSettingsAsync(projectRoot, options) { const newSettings = {}; // Change settings before reading them if (typeof options.https === 'boolean') { newSettings.https = options.https; } if (Object.keys(newSettings).length) { await ProjectSettings().setAsync(projectRoot, newSettings); } return await ProjectSettings().readAsync(projectRoot); } async function getWebpackConfigEnvFromBundlingOptionsAsync(projectRoot, options) { const { dev, https } = await applyOptionsToProjectSettingsAsync(projectRoot, options); const mode = typeof options.mode === 'string' ? options.mode : dev ? 'development' : 'production'; const isImageEditingEnabled = validateBoolOption('isImageEditingEnabled', options.isImageEditingEnabled, true); return { projectRoot, pwa: isImageEditingEnabled, isImageEditingEnabled, mode, https, ...(options.webpackEnv || {}) }; } async function getSSLCertAsync({ name, directory }) { console.log(_chalk().default.magenta`Ensuring auto SSL certificate is created (you might need to re-run with sudo)`); try { const result = await devcert().certificateFor(name); if (result) { const { key, cert } = result; const folder = path().join(directory, '.expo', 'web', 'development', 'ssl'); await _fsExtra().default.ensureDir(folder); const keyPath = path().join(folder, `key-${name}.pem`); await _fsExtra().default.writeFile(keyPath, key); const certPath = path().join(folder, `cert-${name}.pem`); await _fsExtra().default.writeFile(certPath, cert); return { keyPath, certPath }; } return result; } catch (error) { console.log(`Error creating SSL certificates: ${error}`); } return false; } function applyEnvironmentVariables(config) { // Use EXPO_DEBUG_WEB=true to enable debugging features for cases where the prod build // has errors that aren't caught in development mode. // Related: https://github.com/expo/expo-cli/issues/614 if ((0, _WebpackEnvironment().isDebugModeEnabled)() && config.mode === 'production') { console.log(_chalk().default.bgYellow.black('Bundling the project in debug mode.')); const output = config.output || {}; const optimization = config.optimization || {}; // Enable line to line mapped mode for all/specified modules. // Line to line mapped mode uses a simple SourceMap where each line of the generated source is mapped to the same line of the original source. // It’s a performance optimization. Only use it if your performance need to be better and you are sure that input lines match which generated lines. // true enables it for all modules (not recommended) output.devtoolLineToLine = true; // Add comments that describe the file import/exports. // This will make it easier to debug. output.pathinfo = true; // Instead of numeric ids, give modules readable names for better debugging. optimization.namedModules = true; // Instead of numeric ids, give chunks readable names for better debugging. optimization.namedChunks = true; // Readable ids for better debugging. // @ts-ignore Property 'moduleIds' does not exist. optimization.moduleIds = 'named'; // if optimization.namedChunks is enabled optimization.chunkIds is set to 'named'. // This will manually enable it just to be safe. // @ts-ignore Property 'chunkIds' does not exist. optimization.chunkIds = 'named'; if (optimization.splitChunks) { optimization.splitChunks.name = true; } Object.assign(config, { output, optimization }); } return config; } async function invokeWebpackConfigAsync(env, argv) { // Check if the project has a webpack.config.js in the root. const projectWebpackConfig = path().resolve(env.projectRoot, 'webpack.config.js'); let config; if (_fsExtra().default.existsSync(projectWebpackConfig)) { const webpackConfig = require(projectWebpackConfig); if (typeof webpackConfig === 'function') { config = await webpackConfig(env, argv); } else { config = webpackConfig; } } else { // Fallback to the default expo webpack config. const createExpoWebpackConfigAsync = require('@expo/webpack-config'); config = await createExpoWebpackConfigAsync(env, argv); } return applyEnvironmentVariables(config); } async function openProjectAsync(projectRoot) { try { const url = await UrlUtils().constructWebAppUrlAsync(projectRoot, { hostType: 'localhost' }); if (!url) { throw new Error('Webpack Dev Server is not running'); } (0, _openBrowser().default)(url); return { success: true, url }; } catch (e) { _Logger().default.global.error(`Couldn't start project on web: ${e.message}`); return { success: false, error: e }; } } //# sourceMappingURL=__sourcemaps__/Webpack.js.map