"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.kotlinSrcDir = exports.tscOutDir = exports.uvueOutDir = exports.compileApp = void 0;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const kotlin_1 = require("../kotlin");
const stacktrace_1 = require("../stacktrace");
const utils_1 = require("../utils");
const kotlin_2 = require("../stacktrace/kotlin");
const shared_1 = require("../shared");
const DEFAULT_IMPORTS = [
    'io.dcloud.uts.Map',
    'io.dcloud.uts.Set',
    'io.dcloud.uts.UTSAndroid',
    'io.dcloud.uts.*',
    'io.dcloud.uniapp.*',
    'io.dcloud.uniapp.framework.*',
    'io.dcloud.uniapp.vue.*',
    'io.dcloud.uniapp.vue.shared.*',
    'io.dcloud.uniapp.runtime.*',
    'io.dcloud.uniapp.extapi.*',
];
async function compileApp(entry, options) {
    const split = !!options.split;
    const { bundle, UTSTarget } = (0, utils_1.getUTSCompiler)();
    const imports = [...DEFAULT_IMPORTS];
    const isProd = process.env.NODE_ENV !== 'development';
    const { package: pkg, inputDir, outputDir, sourceMap, uni_modules, extApis, autoImports = {}, pages, } = options;
    if ((0, utils_1.shouldAutoImportUniCloud)()) {
        imports.push('io.dcloud.unicloud.*');
    }
    const input = {
        root: inputDir,
        filename: entry,
        paths: {
            vue: 'io.dcloud.uniapp.vue',
            '@dcloudio/uni-app': 'io.dcloud.uniapp.framework',
            '@dcloudio/uni-runtime': 'io.dcloud.uniapp.framework.runtime',
        },
        uniModules: uni_modules,
        uniModulesPrefix: process.env.UNI_UTS_MODULE_PREFIX || '',
        uniXPages: pages,
        globals: {
            envs: {
                ...options.env,
                // 自动化测试
                NODE_ENV: process.env.NODE_ENV,
                UNI_AUTOMATOR_WS_ENDPOINT: process.env.UNI_AUTOMATOR_WS_ENDPOINT || '',
                UNI_AUTOMATOR_APP_WEBVIEW_SRC: process.env.UNI_AUTOMATOR_APP_WEBVIEW_SRC || '',
            },
        },
    };
    const hbxVersion = process.env.HX_Version || process.env.UNI_COMPILER_VERSION;
    const bundleOptions = {
        mode: process.env.NODE_ENV,
        hbxVersion,
        input,
        output: {
            isX: true,
            isSingleThread: true,
            isApp: true,
            isPlugin: false,
            outDir: isProd
                ? kotlinSrcDir(path_1.default.resolve(outputDir, '.uniappx/android/'))
                : kotlinSrcDir((0, kotlin_1.kotlinDir)(outputDir)),
            outFilename: options.outFilename || 'index.kt', // 强制 main.kt => index.kt 因为云端，真机运行识别的都是 index.kt
            package: pkg,
            sourceMap: sourceMap !== false
                ? (0, utils_1.resolveUniAppXSourceMapPath)((0, kotlin_1.kotlinDir)(outputDir))
                : false,
            extname: 'kt',
            imports,
            logFilename: true,
            noColor: true,
            split,
            splitClass: (0, utils_1.isEnableSplitClass)(),
            disableSplitManifest: options.disableSplitManifest,
            uniAppX: {
                uvueOutDir: uvueOutDir('app-android'),
            },
            transform: {
                uniExtApiDefaultNamespace: 'io.dcloud.uniapp.extapi',
                uniExtApiNamespaces: extApis,
                uniExtApiDefaultParameters: (0, utils_1.parseExtApiDefaultParameters)(),
                uniExtApiProviders: options.extApiProviders,
                uvueClassNamePrefix: options.uvueClassNamePrefix || 'Gen',
                uvueGenDefaultAs: '__sfc__',
                uniCloudObjectInfo: options.uniCloudObjectInfo,
                autoImports,
                uniModulesArtifacts: options.uniModulesArtifacts,
                enableUtsNumber: false,
                enableNarrowType: false, // 这里的启用是把部分typeof转换成instanceof，这样确实好一点，但会引发一些kotlin之类的警告，暂不开启
                enableInlineReified: (0, utils_1.isEnableInlineReified)(),
                ...options.transform,
            },
        },
    };
    let hasCache = false;
    if (!isProd) {
        const kotlinRootOutDir = (0, kotlin_1.kotlinDir)(outputDir);
        const kotlinSrcOutDir = kotlinSrcDir(kotlinRootOutDir);
        const manifest = readKotlinManifestJson(kotlinSrcOutDir);
        if (manifest && manifest.env) {
            if (manifest.env.compiler_version !== hbxVersion) {
                // 优先判断版本是否有变更，有变更，清除所有缓存
                fs_extra_1.default.removeSync(kotlinRootOutDir);
            }
            else {
                hasCache = true;
            }
        }
    }
    // const time = Date.now()
    // console.log(bundleOptions)
    const result = await bundle(UTSTarget.KOTLIN, bundleOptions);
    // console.log('UTS编译耗时: ' + (Date.now() - time) + 'ms')
    if (!result) {
        return;
    }
    if (result.error) {
        throw (0, stacktrace_1.parseUTSSyntaxError)(result.error, inputDir);
    }
    if (isProd) {
        const autoImportUniCloud = (0, utils_1.shouldAutoImportUniCloud)();
        const useUniCloudApi = result.inject_apis &&
            result.inject_apis.find((api) => api.startsWith('uniCloud.'));
        if (autoImportUniCloud && !useUniCloudApi) {
            result.inject_apis = result.inject_apis || [];
            result.inject_apis.push('uniCloud.importObject');
        }
        return runKotlinBuild(options, result);
    }
    return runKotlinDev(options, result, hasCache);
}
exports.compileApp = compileApp;
function uvueOutDir(platform) {
    return path_1.default.join(process.env.UNI_APP_X_UVUE_DIR, platform);
}
exports.uvueOutDir = uvueOutDir;
function tscOutDir(platform) {
    return path_1.default.join(process.env.UNI_APP_X_TSC_DIR, platform);
}
exports.tscOutDir = tscOutDir;
function kotlinSrcDir(kotlinDir) {
    return path_1.default.resolve(kotlinDir, 'src');
}
exports.kotlinSrcDir = kotlinSrcDir;
function kotlinDexDir(kotlinDir) {
    return path_1.default.resolve(kotlinDir, 'dex');
}
function kotlinClassDir(kotlinDir) {
    return path_1.default.resolve(kotlinDir, 'class');
}
function resolveDexByKotlinFile(kotlinDexOutDir, kotlinFile) {
    return path_1.default.join(path_1.default.resolve(kotlinDexOutDir, kotlinFile).replace('.kt', ''), 'classes.dex');
}
function parseKotlinChangedFiles(result, kotlinSrcOutDir, kotlinDexOutDir, outputDir) {
    // 解析发生变化的
    const kotlinChangedFiles = result.changed.map((file) => {
        const dexFile = resolveDexByKotlinFile(kotlinDexOutDir, file);
        // 如果kt文件变化，则删除对应的dex文件
        if (fs_extra_1.default.existsSync(dexFile)) {
            fs_extra_1.default.unlinkSync(dexFile);
        }
        return path_1.default.resolve(kotlinSrcOutDir, file);
    });
    ['index.kt', ...(result.chunks || [])].forEach((chunk) => {
        const chunkFile = path_1.default.resolve(kotlinSrcOutDir, chunk);
        if (!kotlinChangedFiles.includes(chunkFile)) {
            const dexFile = resolveDexByKotlinFile(kotlinDexOutDir, chunk);
            if (fs_extra_1.default.existsSync(dexFile)) {
                // 如果缓存的dex文件存在，则不需要重新编译，但需要确定outputDir中存在dex文件
                const targetDexFile = resolveDexByKotlinFile(outputDir, chunk);
                if (!fs_extra_1.default.existsSync(targetDexFile)) {
                    fs_extra_1.default.copySync(dexFile, targetDexFile);
                }
            }
            else {
                kotlinChangedFiles.push(chunkFile);
            }
        }
    });
    return kotlinChangedFiles;
}
function syncDexList(dexList, kotlinDexOutDir, outputDir) {
    dexList.forEach((dex) => {
        const dexFile = path_1.default.resolve(kotlinDexOutDir, dex);
        const targetDexFile = path_1.default.resolve(outputDir, dex);
        fs_extra_1.default.copySync(dexFile, targetDexFile);
    });
}
let isFirst = true;
let checkConfigJsonDeps = true;
async function runKotlinDev(options, result, hasCache) {
    result.type = 'kotlin';
    const { inputDir, outputDir, pageCount, uni_modules } = options;
    const kotlinRootOutDir = (0, kotlin_1.kotlinDir)(outputDir);
    const kotlinDexOutDir = kotlinDexDir(kotlinRootOutDir);
    const kotlinSrcOutDir = kotlinSrcDir(kotlinRootOutDir);
    const kotlinChangedFiles = parseKotlinChangedFiles(result, kotlinSrcOutDir, kotlinDexOutDir, outputDir);
    const kotlinMainFile = path_1.default.resolve(kotlinSrcOutDir, result.filename);
    // 开发模式下，需要生成 dex
    if (fs_extra_1.default.existsSync(kotlinMainFile)) {
        if (!kotlinChangedFiles.length) {
            if (isFirst) {
                isFirst = false;
                console.log(`检测到编译缓存有效，跳过编译。详见：https://uniapp.dcloud.net.cn/uni-app-x/compiler/#cache`);
            }
        }
        else {
            const compilerServer = (0, utils_1.getCompilerServer)('uniapp-runextension');
            if (!compilerServer) {
                throw new Error(`项目使用了uts插件，正在安装 uts Android 运行扩展...`);
            }
            // 检查是否有缓存文件
            if (isFirst) {
                isFirst = false;
                if (hasCache) {
                    console.log(`检测到编译缓存部分失效，开始差量编译。详见：https://uniapp.dcloud.net.cn/uni-app-x/compiler/#cache`);
                }
                // 仅windows
                if (shared_1.isWindows) {
                    console.log(`请在杀毒软件中设置扫描排除名单，减少系统资源消耗。[详情](https://uniapp.dcloud.net.cn/uni-app-x/compiler/#tips)`);
                }
            }
            const { getDefaultJar, getKotlincHome, compile: compileDex, checkDependencies, } = compilerServer;
            const libsJars = (0, kotlin_1.parseUTSModuleLibsJars)(uni_modules);
            let hasError = false;
            const configJsonJars = await (0, kotlin_1.parseUTSModuleConfigJsonJars)(2, uni_modules, checkDependencies, checkConfigJsonDeps, () => {
                hasError = true;
            });
            // 发生错误需要重新check
            checkConfigJsonDeps = hasError;
            // console.log('uni_modules', uni_modules)
            // console.log('libsJars', libsJars)
            // console.log('configJsonJars', configJsonJars)
            const cacheDir = process.env.HX_DEPENDENCIES_DIR || '';
            const kotlinClassOutDir = kotlinClassDir(kotlinRootOutDir);
            const waiting = { done: undefined };
            const compileOptions = {
                version: 'v2',
                pageCount,
                kotlinc: (0, kotlin_1.resolveKotlincArgs)(kotlinChangedFiles, kotlinClassOutDir, getKotlincHome(), [kotlinClassOutDir].concat(getDefaultJar(2)
                    // 加密插件已经迁移到普通插件目录了，理论上不需要了
                    .concat((0, kotlin_1.getUniModulesEncryptCacheJars)(cacheDir)) // 加密插件jar
                    .concat((0, kotlin_1.getUniModulesCacheJars)(cacheDir)) // 普通插件jar
                    .concat((0, kotlin_1.getUniModulesJars)(outputDir)) // cli版本插件jar（没有指定cache的时候）
                    .concat(configJsonJars) // 插件config.json依赖
                    .concat(libsJars) // 插件本地libs
                )).concat(['-module-name', `main-${+Date.now()}`]),
                d8: kotlin_1.D8_DEFAULT_ARGS,
                kotlinOutDir: kotlinClassOutDir,
                dexOutDir: kotlinDexOutDir,
                inputDir: kotlinSrcOutDir,
                stderrListener: (0, kotlin_1.createStderrListener)(kotlinSrcOutDir, (0, utils_1.resolveUniAppXSourceMapPath)(kotlinRootOutDir), waiting, kotlin_2.hbuilderFormatter),
            };
            result.kotlinc = true;
            // console.log('DEX编译参数:', compileOptions)
            const { code, msg, data } = await compileDex(compileOptions, inputDir);
            // 等待 stderrListener 执行完毕
            if (waiting.done) {
                await waiting.done;
            }
            // console.log('DEX编译结果:', code, data)
            if (!code && data) {
                result.changed = data.dexList;
                syncDexList(data.dexList, kotlinDexOutDir, outputDir);
            }
            else {
                // 编译失败，需要调整缓存的 manifest.json
                if (result.changed.length) {
                    const manifest = readKotlinManifestJson(kotlinSrcOutDir);
                    if (manifest && manifest.files) {
                        result.changed.forEach((file) => {
                            delete manifest.files[file];
                        });
                        writeKotlinManifestJson(kotlinSrcOutDir, manifest);
                    }
                    result.changed = [];
                }
                if (msg) {
                    console.error(msg);
                }
            }
        }
    }
    return result;
}
async function runKotlinBuild(options, result) {
    ;
    result.type = 'kotlin';
    (0, utils_1.addPluginInjectComponents)(options.extApiComponents);
    result.inject_modules = (0, utils_1.parseInjectModules)((result.inject_apis || []).concat((0, utils_1.getPluginInjectApis)()), options.extApis || {}, (0, utils_1.getPluginInjectComponents)());
    result.kotlinc = false;
    return result;
}
function readKotlinManifestJson(kotlinSrcOutDir) {
    const file = path_1.default.resolve(kotlinSrcOutDir, '.manifest.json');
    if (fs_extra_1.default.existsSync(file)) {
        return JSON.parse(fs_extra_1.default.readFileSync(file, 'utf8'));
    }
}
function writeKotlinManifestJson(kotlinSrcOutDir, manifest) {
    fs_extra_1.default.writeFileSync(path_1.default.resolve(kotlinSrcOutDir, '.manifest.json'), JSON.stringify(manifest));
}
