"use strict";
/*
 * Tencent is pleased to support the open source community by making
 * Hippy available.
 *
 * Copyright (C) 2017-2019 THL A29 Limited, a Tencent company.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.saveDevPort = exports.normalizeWebpackConfig = exports.getWebpackConfig = void 0;
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const os_1 = tslib_1.__importDefault(require("os"));
const hippy_hmr_plugin_1 = tslib_1.__importDefault(require("@hippy/hippy-hmr-plugin"));
const qrcode_1 = tslib_1.__importDefault(require("qrcode"));
const safe_1 = require("colors/safe");
const log_1 = require("@debug-server-next/utils/log");
const url_1 = require("@debug-server-next/utils/url");
const config_1 = require("@debug-server-next/config");
const ip_1 = require("@debug-server-next/utils/ip");
const port_1 = require("@debug-server-next/utils/port");
const process_1 = require("@debug-server-next/utils/process");
const enum_1 = require("@debug-server-next/@types/enum");
const log_2 = require("@debug-server-next/logger/log");
tslib_1.__exportStar(require("./inject-entry"), exports);
const log = new log_1.Logger('webpack-util');
async function getWebpackConfig(configPath) {
    let webpackConfig;
    const webpackConfigPath = path_1.default.resolve(process.cwd(), configPath);
    log.info('webpackConfigPath: %s', webpackConfigPath);
    if (configPath && fs_1.default.existsSync(webpackConfigPath)) {
        webpackConfig = await (_a = webpackConfigPath, Promise.resolve().then(() => tslib_1.__importStar(require(_a))));
    }
    return (webpackConfig === null || webpackConfig === void 0 ? void 0 : webpackConfig.default) || webpackConfig;
}
exports.getWebpackConfig = getWebpackConfig;
function normalizeWebpackConfig(versionId, config) {
    const enableRemoteDebug = isRemoteDebugEnabled(config);
    if (!enableRemoteDebug)
        return log.warn('remote debug is disabled!');
    normalizeRemoteDebug(versionId, config);
    appendHMRPlugin(versionId, config);
}
exports.normalizeWebpackConfig = normalizeWebpackConfig;
function normalizeRemoteDebug(versionId, config) {
    (0, log_2.info)('Webpack normalizeRemoteDebug ', versionId);
    //热重载live reload
    // 热重载live reload： 就是当修改文件之后，webpack自动编译，然后浏览器自动刷新->等价于页面window.location.reload()
    //
    //热更新HMR
    // 热更新HMR： 热重载live reload并不能够保存应用的状态（states），当刷新页面后，应用之前状态丢失。
    // 而Webpack热更新HMR则不会刷新浏览器，而是运行时对模块进行热替换，保证了应用状态不会丢失。
    config.devServer = {
        // 默认为 false，设为 true 调试服务支持多个工程同时调试，彼此之间不会干扰
        multiple: false,
        // 默认为 false，hippy vue 项目可以手动开启
        vueDevtools: false,
        // 默认 hot, liveReload 都为 true，如果只想使用 live-reload 功能，请将 hot 设为 false，liveReload 设为 true
        hot: true,
        liveReload: true,
        client: {
            overlay: false, //覆在
        },
        autoLaunchHippyDebug: true,
        injectJSDevtools: {
            domains: []
        },
        ...(config.devServer || {}),
    };
    config.devServer.remote = {
        protocol: 'http',
        host: '127.0.0.1',
        port: 38989,
        qrcode: false,
        proxy: '',
        ...(config.devServer.remote || {}),
    };
    config.devServer.remote.port = Number(config.devServer.remote.port);
    //
    if (config.devServer.autoLaunchHippyDebug) {
        autoLaunchHippyDebug(config.devServer.remote);
    }
    const { protocol, host, port, qrcode: qrcodeFn } = config.devServer.remote;
    const publicPath = getPublicPath(versionId, config.devServer);
    config.output.publicPath = publicPath;
    log.info((0, safe_1.bold)((0, safe_1.yellow)(`webpack publicPath is set as: ${config.output.publicPath}`)));
    const ignorePort = couldIgnorePort(protocol, port);
    const bundleUrl = (0, url_1.makeUrl)(`${config.output.publicPath}index.bundle`, {
        debugUrl: (0, url_1.makeUrl)(`${(0, url_1.getWSProtocolByHttpProtocol)(protocol)}://${host}${ignorePort ? '' : `:${port}`}/debugger-proxy`),
    });
    const homeUrl = `${protocol}://${host}${ignorePort ? '' : `:${port}`}/extensions/home.html?hash=${versionId}`;
    config.devServer.cb = () => {
        process.nextTick(() => {
            // only print qrcode when use remote debug server
            const needVersionId = needPublicPathWithVersionId(host, config.multiple);
            if (needVersionId)
                log.info('paste the following bundleUrl in app to open hippy page');
            log.info('bundleUrl: %s', (0, safe_1.bold)((0, safe_1.green)(bundleUrl)));
            log.info('find debug page on: %s', (0, safe_1.bold)((0, safe_1.green)(homeUrl)));
            if (needVersionId && qrcodeFn && typeof qrcodeFn === 'function') {
                const qrcodeStr = qrcodeFn(bundleUrl);
                log.info('bundleUrl QRCode scheme: %s', (0, safe_1.bold)((0, safe_1.green)(qrcodeStr)));
                qrcode_1.default.toString(qrcodeStr, {
                    small: true,
                    type: 'terminal',
                }, (e, qrcodeStr) => {
                    if (e)
                        log.error('draw QRCode of bundleUrl failed: %j', (e === null || e === void 0 ? void 0 : e.stack) || e);
                    log.info('scheme qrcode:\n%s', qrcodeStr);
                });
            }
        });
    };
}
function appendHMRPlugin(versionId, config) {
    const hotManifestPublicPath = getPublicPath(versionId, config.devServer);
    const i = config.plugins.findIndex((plugin) => plugin.constructor.name === hippy_hmr_plugin_1.default.name);
    if (i !== -1) {
        config.plugins.splice(i, 1);
    }
    if (!config.devServer.hot && !config.devServer.liveReload)
        return;
    config.plugins.push(new hippy_hmr_plugin_1.default({ hotManifestPublicPath }));
}
/**
 * remote debug is enabled by default if enable webpack-dev-server
 */
function isRemoteDebugEnabled(webpackConfig) {
    return webpackConfig.mode === 'development' || webpackConfig.devServer;
}
/**
 * when use debug-server-next in local, publicPath will append versionId if set devServer.multiple === true
 * and will always append versionId for remote
 */
function needPublicPathWithVersionId(host, multiple) {
    const isLocal = host === 'localhost' || host === '127.0.0.1';
    if (isLocal)
        return Boolean(multiple);
    return true;
}
function getPublicPath(versionId, { remote: { host, port, protocol }, multiple }) {
    const ignorePort = couldIgnorePort(protocol, port);
    if (!needPublicPathWithVersionId(host, multiple))
        return `${protocol}://${host}${ignorePort ? '' : `:${port}`}/`;
    return `${protocol}://${host}${ignorePort ? '' : `:${port}`}/${versionId}/`;
}
function couldIgnorePort(protocol, port) {
    const couldIgnore = (protocol === 'https' && Number(port) === 443) || (protocol === 'http' && Number(port) === 80);
    log.silly(couldIgnore);
    // TODO iOS doesn't support ignore port
    return false;
}
/**
 * hippy-dev process will save hmrPort to file,
 * hippy-debug process will auto reverse hmrPort when device reconnect.
 */
async function saveDevPort(hmrPort) {
    const { cachePath } = config_1.config;
    fs_1.default.mkdirSync(cachePath, { recursive: true });
    return fs_1.default.writeFileSync(config_1.config.hmrPortPath, String(hmrPort));
}
exports.saveDevPort = saveDevPort;
/**
 * auto launch hippy:debug if remote.host is local hostname
 */
async function autoLaunchHippyDebug({ protocol, host, port }) {
    if (protocol !== 'http')
        return;
    (0, log_2.info)("start debug server start...");
    const hostList = await (0, ip_1.getAllLocalHostname)();
    (0, log_2.info)("AllLocalHostname:", hostList);
    if (!hostList.includes(host))
        return;
    const inUse = await (0, port_1.isPortInUse)(port);
    (0, log_2.info)(host + " isPortInUse:", inUse);
    if (inUse) {
        const osType = os_1.default.type();
        let checkPortCMD;
        if (osType === enum_1.OSType.Darwin) {
            checkPortCMD = `lsof -i :${port}`;
        }
        else if (osType === enum_1.OSType.Windows) {
            checkPortCMD = `netstat -aon|findstr "${port}"`;
        }
        return log.warn((0, safe_1.bold)((0, safe_1.yellow)(`es-debug port ${port} is in use, please check by run "${checkPortCMD}"`)));
    }
    (0, log_2.info)("exec es debug.js...");
    const cp = await (0, process_1.exec)('node', [path_1.default.join(__dirname, '../../../dist/index-debug.js'), '--port', port]);
    if (!global.__CHILD_PROCESS__) {
        global.__CHILD_PROCESS__ = [];
    }
    global.__CHILD_PROCESS__.push(cp);
    //
    (0, log_2.info)("child process list size:", global.__CHILD_PROCESS__.length);
    (0, log_2.info)("start debug server end...");
}
