"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports._BabelTransformCalsExpression = exports.buildH5App = void 0;
const path_1 = __importDefault(require("path"));
const weapps_core_1 = require("@cloudbase/lowcode-generator/lib/weapps-core");
const material_1 = require("./material");
const generate_1 = require("./generate");
const webpack_1 = require("./webpack");
const webpack_2 = require("../service/webpack");
const npm_1 = require("./npm");
const copy_1 = require("./copy");
const common_1 = require("../types/common");
const compile_1 = require("./compile");
const util_1 = require("../util");
const types_1 = require("../../types");
const mp_1 = require("../mp");
const common_2 = require("../../utils/common");
const config_1 = require("../config");
const fs_extra_1 = __importDefault(require("fs-extra"));
const junk = __importStar(require("../util/junk"));
const url_1 = require("url");
let BabelTransform = (code, options) => code;
if (!process.env.IS_WEB_WORKER) {
    let transform = require('../../utils/transform');
    BabelTransform = transform.BabelTransform;
}
async function buildH5App({ buildContext, i18nConfig, extraData, cals: _cals, buildTypeList = [common_1.BuildType.WEB], subAppCalsList, mode, devTool, runtime = types_1.RUNTIME.NONE, deployOptions = { mode: types_1.DEPLOY_MODE.PREVIEW }, ignoreInstall = false, cdnEndpoints, }) {
    var _a, _b, _c;
    const { projDir: buildDir, domain, materialLibs: dependencies, appId: appKey, isPrivateMode, enableExpiredTag, } = buildContext;
    if (!deployOptions.mode) {
        deployOptions.mode = types_1.DEPLOY_MODE.PREVIEW;
    }
    try {
        const { materialsDir } = runtime === types_1.RUNTIME.CI ? (0, util_1.getCompileDirs)(appKey) : (0, util_1.getCompileDirs)('app');
        const h5BuildDir = path_1.default.join(buildDir, 'h5');
        if (buildTypeList.includes(common_1.BuildType.APP) ||
            buildTypeList.includes(common_1.BuildType.XPAGE_PC) ||
            buildTypeList.includes(common_1.BuildType.ADMIN_PORTAL)) {
            if (!_cals.extra) {
                _cals.extra = {};
            }
            _cals.extra.historyType = types_1.HISTORY_TYPE.HASH;
        }
        const cals = processCals(_cals);
        const mainAppSerializeData = (0, common_2.processCals2WeappsData)(cals, dependencies);
        const subAppSerializeDataList = (subAppCalsList === null || subAppCalsList === void 0 ? void 0 : subAppCalsList.map((item) => (0, common_2.processCals2WeappsData)(item, dependencies))) || [];
        const { allAppUsedComps } = (0, mp_1.handleUsedComponents)({
            buildContext: { ...buildContext, mainAppData: mainAppSerializeData },
            weapps: [mainAppSerializeData, ...subAppSerializeDataList],
        });
        // 处理应用数据
        const mainAppData = (0, weapps_core_1.deserialize)(mainAppSerializeData);
        if (!mainAppData.extra) {
            mainAppData.extra = { domain };
        }
        if (!((_a = mainAppData.extra) === null || _a === void 0 ? void 0 : _a.domain)) {
            mainAppData.extra.domain = domain;
        }
        const subAppDataList = subAppSerializeDataList.map((sub) => (0, weapps_core_1.deserialize)(sub));
        // 前置操作
        const { publicPath, basename, assets = '', _indexPage } = ((_b = mainAppData.appConfig) === null || _b === void 0 ? void 0 : _b.window) || {};
        // 获取 插入的cdn 资源
        const jsAssets = await (0, generate_1.handleAssets)({
            appBuildDir: buildDir,
            buildTypeList,
            assets,
        });
        await (0, copy_1.runCopy)(h5BuildDir, mainAppData, i18nConfig);
        // // 素材库
        const runHandleMaterialTag = '🚥  buildWebApp-runHandleMaterial';
        console.time(runHandleMaterialTag);
        // 精简只使用用到的组件库，并获取补齐json格式
        const processedDependencies = await (0, material_1.runHandleMaterial)(h5BuildDir, dependencies.filter((lib) => {
            // 注意需要包涵官方的(gsd)，部分平台方法需要由其补充
            return lib.name === config_1.OFFICIAL_LIB_KEY || !!allAppUsedComps[lib.name];
        }), materialsDir, runtime, ignoreInstall);
        console.timeEnd(runHandleMaterialTag);
        // // 获取 generate 需要的构建文件
        const runGenerateTag = '🚥  buildWebApp-generateProjectFiles';
        console.time(runGenerateTag);
        await (0, generate_1.generateProjectFiles)({
            dependencies: processedDependencies,
            cals,
            mainAppData,
            subAppDataList,
            appKey,
            publicPath,
            i18nConfig,
            extraData,
            basename,
            appBuildDir: h5BuildDir,
            buildTypeList,
            isSandbox: false,
            runtime,
            deployOptions,
            _indexPage,
            isPrivateMode,
            expirationStartTimesnap: enableExpiredTag ? Date.now() : undefined,
        });
        console.timeEnd(runGenerateTag);
        // // 构建 NPM 包
        await (0, npm_1.handleNPM)({
            appKey,
            mainAppData,
            subAppDataList,
            appBuildDir: h5BuildDir,
            dependencies: processedDependencies,
            mode,
            devTool,
            ignoreInstall,
        });
        // 生成主题文件
        await (0, generate_1.generateThemeVarsFile)(mainAppData.themeVars, h5BuildDir);
        // 生成 webpack 配置
        const webpackConfigPath = await (0, webpack_1.runWebpackCore)({
            cals,
            appKey,
            mainAppData,
            subAppDataList,
            appBuildDir: h5BuildDir,
            assets: jsAssets,
            buildTypeList,
            mode,
            devTool,
            publicPath,
            domain,
            cdnEndpoints,
        });
        await new Promise((resolve, reject) => {
            // 开始编译前清理一下 lowcode 内容
            (0, compile_1.startCompile)({
                appBuildDir: h5BuildDir,
                configPath: webpackConfigPath,
                appKey,
                webpackMode: mode,
            }, function (err, data) {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(data);
                }
            });
        });
        const MAINIFAST_FILENAME = 'weda-manifest.json';
        const preHeatUrls = fs_extra_1.default
            .readdirSync(path_1.default.resolve(h5BuildDir, webpack_2.OUTPUT_DIR))
            .filter((file) => junk.not(file) && file !== MAINIFAST_FILENAME)
            .map((file) => {
            if (/LICENSE\.txt$/.test(file) || /\.map$/.test(file)) {
                return '';
            }
            try {
                const url = new url_1.URL(publicPath);
                return `${url.origin}${path_1.default.posix.join(url.pathname, file)}`;
            }
            catch (e) {
                return path_1.default.posix.join(publicPath || '', file);
            }
        })
            .filter((item) => !!item);
        // 普通 web 模式，且非hash模式，根据页面生成多份入口
        if (!((_c = cals.extra) === null || _c === void 0 ? void 0 : _c.historyType) || cals.extra.historyType === types_1.HISTORY_TYPE.BROWSER) {
            preHeatUrls.push(path_1.default.posix.join(basename || ''));
            await Promise.all((cals.items || []).map((page) => {
                preHeatUrls.push(path_1.default.posix.join(basename || '', page.id));
                preHeatUrls.push(path_1.default.posix.join(basename || '', page.id, '/'));
                return fs_extra_1.default.copy(path_1.default.resolve(h5BuildDir, webpack_2.OUTPUT_DIR, 'index.html'), path_1.default.resolve(h5BuildDir, webpack_2.OUTPUT_DIR, page.id, 'index.html'));
            }));
        }
        await fs_extra_1.default.writeFile(path_1.default.resolve(h5BuildDir, webpack_2.OUTPUT_DIR, 'weda-manifest.json'), JSON.stringify({ preHeatUrls }, undefined, 2));
        return h5BuildDir;
    }
    catch (e) {
        // console.error('构建失败：', e);
        throw e;
    }
}
exports.buildH5App = buildH5App;
function processCals(cals) {
    return walkThroughJson(cals, (key, value, prefix) => {
        return _BabelTransformCalsExpression(key, value);
    });
}
function _isPlainObj(obj) {
    return typeof obj === 'object' && obj !== null;
}
function walkThroughJson(obj, callback, path = '') {
    /**
     * 跳过数据源 state 的处理
     */
    if (/dataset\.state$/.test(path)) {
        return obj;
    }
    if (typeof obj === 'object' && Array.isArray(obj)) {
        return obj.map((item, index) => {
            const prefix = path ? `${path}.${index}` : String(index);
            return _isPlainObj(item) ? walkThroughJson(item, callback, prefix) : callback(index, item, prefix);
        });
    }
    for (const key in obj) {
        const value = obj[key];
        const prefix = path ? `${path}.${key}` : key;
        obj[key] = _isPlainObj(value) ? walkThroughJson(value, callback, prefix) : callback(key, value, prefix);
    }
    return obj;
}
/**
 * 导出进行单测
 */
function _BabelTransformCalsExpression(key, value) {
    const DYNAMIC_REG = /^:(.*)$/;
    const FOR_IN_REG = /^(.*?in\s)/;
    if (DYNAMIC_REG.test(key)) {
        try {
            let forPrefix = '';
            if (key === ':for') {
                const matched = value.match(FOR_IN_REG) || [];
                forPrefix = matched[0];
                if (forPrefix) {
                    value = value.replace(FOR_IN_REG, '');
                }
            }
            value = BabelTransform(value, { IIFE: true });
            if (forPrefix) {
                value = `${forPrefix}${value}`;
            }
            return value;
        }
        catch (e) {
            return value;
        }
    }
    return value;
}
exports._BabelTransformCalsExpression = _BabelTransformCalsExpression;
