// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.waitUntilPackagerIsRunning = exports.waitUntilDevServerIsRunning = exports.waitUntil = exports.startDebugging = exports.runPackager = exports.runNodeDebugger = exports.runDevServer = exports.parsePlatform = exports.parseDebuggingMethod = exports.isPackagerRunning = exports.isDevServerRunning = exports.Platform = exports.AppType = void 0; const AdmZip = require("adm-zip"); const fetch = require("node-fetch"); const fs = require("fs"); const fspath = require("path"); const devCerts = require("office-addin-dev-certs"); const devSettings = require("office-addin-dev-settings"); const os = require("os"); const office_addin_dev_settings_1 = require("office-addin-dev-settings"); const office_addin_manifest_1 = require("office-addin-manifest"); const nodeDebugger = require("office-addin-node-debugger"); const debugInfo = require("./debugInfo"); const port_1 = require("./port"); const process_1 = require("./process"); const defaults_1 = require("./defaults"); const office_addin_usage_data_1 = require("office-addin-usage-data"); /* global process, console, setTimeout */ var AppType; (function (AppType) { AppType["Desktop"] = "desktop"; AppType["Web"] = "web"; })(AppType = exports.AppType || (exports.AppType = {})); var Platform; (function (Platform) { Platform["Android"] = "android"; Platform["Desktop"] = "desktop"; Platform["iOS"] = "ios"; Platform["MacOS"] = "macos"; Platform["Win32"] = "win32"; Platform["Web"] = "web"; })(Platform = exports.Platform || (exports.Platform = {})); function defaultDebuggingMethod() { return office_addin_dev_settings_1.DebuggingMethod.Direct; } function delay(milliseconds) { return new Promise((resolve) => { setTimeout(resolve, milliseconds); }); } function isDevServerRunning(port) { return __awaiter(this, void 0, void 0, function* () { // isPortInUse(port) will return false when webpack-dev-server is running. // it should be fixed, but for now, use getProcessIdsForPort(port) const processIds = yield (0, port_1.getProcessIdsForPort)(port); const isRunning = processIds.length > 0; return isRunning; }); } exports.isDevServerRunning = isDevServerRunning; function isPackagerRunning(statusUrl) { return __awaiter(this, void 0, void 0, function* () { const statusRunningResponse = `packager-status:running`; try { const response = yield fetch.default(statusUrl); console.log(`packager: ${response.status} ${response.statusText}`); const text = yield response.text(); console.log(`packager: ${text}`); return statusRunningResponse === text; } catch (err) { return false; } }); } exports.isPackagerRunning = isPackagerRunning; function parseDebuggingMethod(text) { switch (text) { case "direct": return office_addin_dev_settings_1.DebuggingMethod.Direct; case "proxy": return office_addin_dev_settings_1.DebuggingMethod.Proxy; default: return undefined; } } exports.parseDebuggingMethod = parseDebuggingMethod; function parsePlatform(text) { if (text === AppType.Desktop) { text = process.platform; } switch (text) { case "android": return Platform.Android; case "darwin": return Platform.MacOS; case "ios": return Platform.iOS; case "macos": return Platform.MacOS; case "web": return Platform.Web; case "win32": return Platform.Win32; default: throw new office_addin_usage_data_1.ExpectedError(`The current platform is not supported: ${text}`); } } exports.parsePlatform = parsePlatform; function runDevServer(commandLine, port) { return __awaiter(this, void 0, void 0, function* () { if (commandLine) { // if the dev server is running if (port !== undefined && (yield isDevServerRunning(port))) { console.log(`The dev server is already running on port ${port}.`); } else { // On non-Windows platforms, prompt for installing the dev certs before starting the dev server. // This is a workaround for the fact that the detached process does not show a window on Mac, // therefore the user cannot enter the password when prompted. if (process.platform !== "win32") { if (!devCerts.verifyCertificates()) { yield devCerts.ensureCertificatesAreInstalled(); } } // start the dev server console.log(`Starting the dev server... (${commandLine})`); const devServerProcess = (0, process_1.startDetachedProcess)(commandLine); yield debugInfo.saveDevServerProcessId(devServerProcess.pid); if (port !== undefined) { // wait until the dev server is running const isRunning = yield waitUntilDevServerIsRunning(port); if (isRunning) { console.log(`The dev server is running on port ${port}. Process id: ${devServerProcess.pid}`); } else { throw new Error(`The dev server is not running on port ${port}.`); } } } } }); } exports.runDevServer = runDevServer; function runNodeDebugger(host, port) { return __awaiter(this, void 0, void 0, function* () { nodeDebugger.run(host, port); console.log("The node debugger is running."); }); } exports.runNodeDebugger = runNodeDebugger; function runPackager(commandLine, host = "localhost", port = "8081") { return __awaiter(this, void 0, void 0, function* () { if (commandLine) { const packagerUrl = `http://${host}:${port}`; const statusUrl = `${packagerUrl}/status`; // if the packager is running if (yield isPackagerRunning(statusUrl)) { console.log(`The packager is already running. ${packagerUrl}`); } else { // start the packager console.log(`Starting the packager... (${commandLine})`); yield (0, process_1.startDetachedProcess)(commandLine); // wait until the packager is running if (yield waitUntilPackagerIsRunning(statusUrl)) { console.log(`The packager is running. ${packagerUrl}`); } else { throw new Error(`The packager is not running. ${packagerUrl}`); } } } }); } exports.runPackager = runPackager; /** * Start debugging * @param options startDebugging options. */ function startDebugging(manifestPath, options) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { const { appType, app, debuggingMethod, sourceBundleUrlComponents, devServerCommandLine, devServerPort, packagerCommandLine, packagerHost, packagerPort, enableDebugging, enableLiveReload, enableSideload, openDevTools, document, } = Object.assign(Object.assign({}, options), { // Defaults when variable is undefined. debuggingMethod: options.debuggingMethod || defaultDebuggingMethod(), enableDebugging: (_a = options.enableDebugging) !== null && _a !== void 0 ? _a : true, enableSideload: (_b = options.enableSideload) !== null && _b !== void 0 ? _b : true, enableLiveReload: (_c = options.enableLiveReload) !== null && _c !== void 0 ? _c : true }); try { if (appType === undefined) { throw new office_addin_usage_data_1.ExpectedError("Please specify the application type to debug."); } const isWindowsPlatform = process.platform === "win32"; const isDesktopAppType = appType === AppType.Desktop; const isProxyDebuggingMethod = debuggingMethod === office_addin_dev_settings_1.DebuggingMethod.Proxy; // live reload can only be enabled for the desktop app type // when using proxy debugging and the packager const canEnableLiveReload = isDesktopAppType && isProxyDebuggingMethod && !!packagerCommandLine; // only use live reload if enabled and it can be enabled const useLiveReload = enableLiveReload && canEnableLiveReload; console.log(enableDebugging ? "Debugging is being started..." : "Starting without debugging..."); console.log(`App type: ${appType}`); if (manifestPath.endsWith(".zip")) { manifestPath = yield extractManifest(manifestPath); } const manifestInfo = yield office_addin_manifest_1.OfficeAddinManifest.readManifestFile(manifestPath); if (!manifestInfo.id) { throw new office_addin_usage_data_1.ExpectedError("Manifest does not contain the id for the Office Add-in."); } // enable loopback for Edge if (isWindowsPlatform && parseInt(os.release(), 10) === 10) { const name = isDesktopAppType ? "EdgeWebView" : "EdgeWebBrowser"; try { yield devSettings.ensureLoopbackIsEnabled(name); } catch (err) { // if add loopback exemption failed, report the error then continue console.error(err); console.warn("Failed to add loopback exemption.\nWill try to sideload the Office Add-in without the loopback exemption, but it might not load correctly from localhost.\n"); defaults_1.usageDataObject.reportException("startDebugging()", err, { app: app, document: document, appType: appType, }); } } // enable debugging if (isDesktopAppType && isWindowsPlatform) { yield devSettings.enableDebugging(manifestInfo.id, enableDebugging, debuggingMethod, openDevTools); if (enableDebugging) { console.log(`Enabled debugging for add-in ${manifestInfo.id}.`); } } // enable live reload if (isDesktopAppType && isWindowsPlatform) { yield devSettings.enableLiveReload(manifestInfo.id, useLiveReload); if (useLiveReload) { console.log(`Enabled live-reload for add-in ${manifestInfo.id}.`); } } // set source bundle url if (isDesktopAppType && isWindowsPlatform) { if (sourceBundleUrlComponents) { yield devSettings.setSourceBundleUrl(manifestInfo.id, sourceBundleUrlComponents); } } // Run packager and dev server at the same time and wait for them to complete. let packagerPromise; let devServerPromise; if (packagerCommandLine && isProxyDebuggingMethod && isDesktopAppType) { packagerPromise = runPackager(packagerCommandLine, packagerHost, packagerPort); } if (devServerCommandLine) { devServerPromise = runDevServer(devServerCommandLine, devServerPort); } if (packagerPromise !== undefined) { try { yield packagerPromise; } catch (err) { console.log(`Unable to start the packager. ${err}`); } } if (devServerPromise !== undefined) { try { yield devServerPromise; } catch (err) { console.log(`Unable to start the dev server. ${err}`); } } if (enableDebugging && isProxyDebuggingMethod && isDesktopAppType) { try { yield runNodeDebugger(); } catch (err) { console.log(`Unable to start the node debugger. ${err}`); } } if (enableSideload) { try { console.log(`Sideloading the Office Add-in...`); yield (0, office_addin_dev_settings_1.sideloadAddIn)(manifestPath, app, true, appType, document); } catch (err) { throw new Error(`Unable to sideload the Office Add-in. \n${err}`); } } console.log(enableDebugging ? "Debugging started." : "Started."); defaults_1.usageDataObject.reportSuccess("startDebugging()", { app: app, document: document, appType: appType, }); } catch (err) { defaults_1.usageDataObject.reportException("startDebugging()", err, { app: app, document: document, appType: appType, }); throw err; } }); } exports.startDebugging = startDebugging; function waitUntil(callback, retryCount, retryDelay) { return __awaiter(this, void 0, void 0, function* () { let done = yield callback(); while (!done && retryCount) { --retryCount; yield delay(retryDelay); done = yield callback(); } return done; }); } exports.waitUntil = waitUntil; function waitUntilDevServerIsRunning(port, retryCount = 30, retryDelay = 1000) { return __awaiter(this, void 0, void 0, function* () { return waitUntil(() => __awaiter(this, void 0, void 0, function* () { return yield isDevServerRunning(port); }), retryCount, retryDelay); }); } exports.waitUntilDevServerIsRunning = waitUntilDevServerIsRunning; function waitUntilPackagerIsRunning(statusUrl, retryCount = 30, retryDelay = 1000) { return __awaiter(this, void 0, void 0, function* () { return waitUntil(() => __awaiter(this, void 0, void 0, function* () { return yield isPackagerRunning(statusUrl); }), retryCount, retryDelay); }); } exports.waitUntilPackagerIsRunning = waitUntilPackagerIsRunning; function extractManifest(zipPath) { return __awaiter(this, void 0, void 0, function* () { const targetPath = fspath.join(process.env.TEMP, "addinManifest"); const zip = new AdmZip(zipPath); // reading archives zip.extractAllTo(targetPath, true); // overwrite const manifestPath = fspath.join(targetPath, "manifest.json"); if (fs.existsSync(manifestPath)) { return manifestPath; } else { throw new Error(`The zip file '${zipPath}' does not contain a "manifest.json" file`); } }); } //# sourceMappingURL=start.js.map