@expo/cli
Version:
315 lines (314 loc) • 12.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
WebpackBundlerDevServer: ()=>WebpackBundlerDevServer,
getProjectWebpackConfigFilePath: ()=>getProjectWebpackConfigFilePath
});
function _chalk() {
const data = /*#__PURE__*/ _interopRequireDefault(require("chalk"));
_chalk = function() {
return data;
};
return data;
}
function _fs() {
const data = /*#__PURE__*/ _interopRequireDefault(require("fs"));
_fs = function() {
return data;
};
return data;
}
function _path() {
const data = /*#__PURE__*/ _interopRequireWildcard(require("path"));
_path = function() {
return data;
};
return data;
}
function _resolveFrom() {
const data = /*#__PURE__*/ _interopRequireDefault(require("resolve-from"));
_resolveFrom = function() {
return data;
};
return data;
}
const _compile = require("./compile");
const _resolveFromProject = require("./resolveFromProject");
const _tls = require("./tls");
const _log = /*#__PURE__*/ _interopRequireWildcard(require("../../../log"));
const _env = require("../../../utils/env");
const _errors = require("../../../utils/errors");
const _ip = require("../../../utils/ip");
const _nodeEnv = require("../../../utils/nodeEnv");
const _port = require("../../../utils/port");
const _progress = require("../../../utils/progress");
const _dotExpo = require("../../project/dotExpo");
const _bundlerDevServer = require("../BundlerDevServer");
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interopRequireWildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && 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 debug = require("debug")("expo:start:server:webpack:devServer");
function assertIsWebpackDevServer(value) {
if (!(value == null ? void 0 : value.sockWrite) && !(value == null ? void 0 : value.sendMessage)) {
var ref;
var ref1;
throw new _errors.CommandError("WEBPACK", value ? "Expected Webpack dev server, found: " + ((ref1 = (ref = value.constructor) == null ? void 0 : ref.name) != null ? ref1 : value) : "Webpack dev server not started yet.");
}
}
class WebpackBundlerDevServer extends _bundlerDevServer.BundlerDevServer {
get name() {
return "webpack";
}
async startTypeScriptServices() {
// noop -- this feature is Metro-only.
}
broadcastMessage(method, params) {
var ref;
if (!this.instance) {
return;
}
assertIsWebpackDevServer((ref = this.instance) == null ? void 0 : ref.server);
// TODO(EvanBacon): Custom Webpack overlay.
// Default webpack-dev-server sockets use "content-changed" instead of "reload" (what we use on native).
// For now, just manually convert the value so our CLI interface can be unified.
const hackyConvertedMessage = method === "reload" ? "content-changed" : method;
if ("sendMessage" in this.instance.server) {
// @ts-expect-error: https://github.com/expo/expo/issues/21994#issuecomment-1517122501
this.instance.server.sendMessage(this.instance.server.sockets, hackyConvertedMessage, params);
} else {
this.instance.server.sockWrite(this.instance.server.sockets, hackyConvertedMessage, params);
}
}
isTargetingNative() {
return false;
}
async getAvailablePortAsync(options) {
try {
var ref;
const defaultPort = (ref = options == null ? void 0 : options.defaultPort) != null ? ref : 19006;
const port = await (0, _port.choosePortAsync)(this.projectRoot, {
defaultPort,
host: _env.env.WEB_HOST
});
if (!port) {
throw new _errors.CommandError("NO_PORT_FOUND", `Port ${defaultPort} not available.`);
}
return port;
} catch (error) {
throw new _errors.CommandError("NO_PORT_FOUND", error.message);
}
}
async bundleAsync({ mode , clear }) {
// Do this first to fail faster.
const webpack = (0, _resolveFromProject.importWebpackFromProject)(this.projectRoot);
if (clear) {
await this.clearWebProjectCacheAsync(this.projectRoot, mode);
}
const config = await this.loadConfigAsync({
isImageEditingEnabled: true,
mode
});
if (!config.plugins) {
config.plugins = [];
}
const bar = (0, _progress.createProgressBar)((0, _chalk().default)`{bold Web} Bundling Javascript [:bar] :percent`, {
width: 64,
total: 100,
clear: true,
complete: "=",
incomplete: " "
});
// NOTE(EvanBacon): Add a progress bar to the webpack logger if defined (e.g. not in CI).
if (bar != null) {
config.plugins.push(new webpack.ProgressPlugin((percent)=>{
bar == null ? void 0 : bar.update(percent);
if (percent === 1) {
bar == null ? void 0 : bar.terminate();
}
}));
}
// Create a webpack compiler that is configured with custom messages.
const compiler = webpack(config);
try {
await (0, _compile.compileAsync)(compiler);
} catch (error) {
_log.error(_chalk().default.red("Failed to compile"));
throw error;
} finally{
bar == null ? void 0 : bar.terminate();
}
}
async startImplementationAsync(options) {
// Do this first to fail faster.
const webpack = (0, _resolveFromProject.importWebpackFromProject)(this.projectRoot);
const WebpackDevServer = (0, _resolveFromProject.importWebpackDevServerFromProject)(this.projectRoot);
await this.stopAsync();
options.port = await this.getAvailablePortAsync({
defaultPort: options.port
});
const { resetDevServer , https , port , mode } = options;
this.urlCreator = this.getUrlCreator({
port,
location: {
scheme: https ? "https" : "http"
}
});
debug("Starting webpack on port: " + port);
if (resetDevServer) {
await this.clearWebProjectCacheAsync(this.projectRoot, mode);
}
if (https) {
debug("Configuring TLS to enable HTTPS support");
await (0, _tls.ensureEnvironmentSupportsTLSAsync)(this.projectRoot).catch((error)=>{
_log.error(`Error creating TLS certificates: ${error}`);
});
}
const config = await this.loadConfigAsync(options);
_log.log((0, _chalk().default)`Starting Webpack on port ${port} in {underline ${mode}} mode.`);
// Create a webpack compiler that is configured with custom messages.
const compiler = webpack(config);
const server = new WebpackDevServer(// @ts-expect-error: type mismatch -- Webpack types aren't great.
compiler, config.devServer);
// Launch WebpackDevServer.
server.listen(port, _env.env.WEB_HOST, function(error) {
if (error) {
_log.error(error.message);
}
});
// Extend the close method to ensure that we clean up the local info.
const originalClose = server.close.bind(server);
server.close = (callback)=>{
return originalClose((err)=>{
this.instance = null;
callback == null ? void 0 : callback(err);
});
};
const _host = (0, _ip.getIpAddress)();
const protocol = https ? "https" : "http";
return {
// Server instance
server,
// URL Info
location: {
url: `${protocol}://${_host}:${port}`,
port,
protocol,
host: _host
},
middleware: null,
// Match the native protocol.
messageSocket: {
broadcast: this.broadcastMessage
}
};
}
/** Load the Webpack config. Exposed for testing. */ getProjectConfigFilePath() {
var ref;
// Check if the project has a webpack.config.js in the root.
return (ref = this.getConfigModuleIds().reduce((prev, moduleId)=>prev || _resolveFrom().default.silent(this.projectRoot, moduleId), null)) != null ? ref : null;
}
async loadConfigAsync(options, argv) {
// let bar: ProgressBar | null = null;
const env = {
projectRoot: this.projectRoot,
pwa: !!options.isImageEditingEnabled,
// TODO: Use a new loader in Webpack config...
logger: {
info () {}
},
mode: options.mode,
https: options.https
};
var _mode;
(0, _nodeEnv.setNodeEnv)((_mode = env.mode) != null ? _mode : "development");
require("@expo/env").load(env.projectRoot);
// Check if the project has a webpack.config.js in the root.
const projectWebpackConfig = this.getProjectConfigFilePath();
let config;
if (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 loadDefaultConfigAsync = (0, _resolveFromProject.importExpoWebpackConfigFromProject)(this.projectRoot);
// @ts-expect-error: types appear to be broken
config = await loadDefaultConfigAsync(env, argv);
}
return config;
}
getConfigModuleIds() {
return [
"./webpack.config.js"
];
}
async clearWebProjectCacheAsync(projectRoot, mode = "development") {
_log.log(_chalk().default.dim(`Clearing Webpack ${mode} cache directory...`));
const dir = await (0, _dotExpo.ensureDotExpoProjectDirectoryInitialized)(projectRoot);
const cacheFolder = _path().join(dir, "web/cache", mode);
try {
await _fs().default.promises.rm(cacheFolder, {
recursive: true,
force: true
});
} catch (error) {
_log.error(`Could not clear ${mode} web cache directory: ${error.message}`);
}
}
}
function getProjectWebpackConfigFilePath(projectRoot) {
return _resolveFrom().default.silent(projectRoot, "./webpack.config.js");
}
//# sourceMappingURL=WebpackBundlerDevServer.js.map
;