"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 __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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MiniProgramApplicationAnalysisService = void 0;
const core_1 = require("@angular-devkit/core");
const paths_1 = require("@ngtools/webpack/src/ivy/paths");
const crypto_1 = require("crypto");
const cyia_code_util_1 = require("cyia-code-util");
const path = __importStar(require("path"));
const static_injector_1 = require("static-injector");
const typescript_1 = __importDefault(require("typescript"));
const library_1 = require("../library");
const mini_program_compiler_1 = require("../mini-program-compiler");
const platform_1 = require("../platform/platform");
const load_esm_1 = require("../util/load_esm");
const token_1 = require("./token");
const i0 = __importStar(require("static-injector"));
const i1 = __importStar(require("../platform/platform"));
class MiniProgramApplicationAnalysisService {
    constructor(injector, compilation, system, compiler, tsConfig, oldBuilder, pagePatternList, buildPlatform) {
        this.injector = injector;
        this.compilation = compilation;
        this.system = system;
        this.compiler = compiler;
        this.tsConfig = tsConfig;
        this.oldBuilder = oldBuilder;
        this.pagePatternList = pagePatternList;
        this.buildPlatform = buildPlatform;
        this.dependencyUseModule = new Map();
        this.cleanDependencyFileCacheSet = new Set();
    }
    exportComponentBuildMetaMap() {
        return __awaiter(this, void 0, void 0, function* () {
            const injector = static_injector_1.Injector.create({
                providers: [
                    {
                        provide: mini_program_compiler_1.MiniProgramCompilerService,
                        useFactory: (injector, buildPlatform) => {
                            return new mini_program_compiler_1.MiniProgramCompilerService(this.ngTscProgram, injector, buildPlatform);
                        },
                        deps: [static_injector_1.Injector, platform_1.BuildPlatform],
                    },
                ],
                parent: this.injector,
            });
            const miniProgramCompilerService = injector.get(mini_program_compiler_1.MiniProgramCompilerService);
            miniProgramCompilerService.init();
            const metaMap = yield miniProgramCompilerService.exportComponentBuildMetaMap();
            const selfMetaCollection = metaMap.otherMetaCollectionGroup['$self'];
            const selfTemplate = {};
            if (selfMetaCollection) {
                const importSelfTemplatePath = `/self-template/self${this.buildPlatform.fileExtname.contentTemplate}`;
                const importSelfTemplate = `<import src="${importSelfTemplatePath}"/>`;
                metaMap.outputContent.forEach((value, key) => {
                    value = `${importSelfTemplate}${value}`;
                    metaMap.outputContent.set(key, value);
                });
                metaMap.useComponentPath.forEach((value, key) => {
                    value.libraryPath.push(...selfMetaCollection.libraryPath);
                    value.localPath.push(...selfMetaCollection.localPath);
                });
                selfTemplate[importSelfTemplatePath] = selfMetaCollection.templateList
                    .map((item) => item.content)
                    .join('');
                delete metaMap.otherMetaCollectionGroup['$self'];
            }
            metaMap.useComponentPath.forEach((value, key) => {
                value.libraryPath = Array.from(new Set(value.libraryPath));
                value.localPath = Array.from(new Set(value.localPath));
            });
            const styleMap = new Map();
            metaMap.style.forEach((value, key) => {
                const entryPattern = this.getComponentPagePattern(key);
                styleMap.set(entryPattern.outputFiles.style, value);
            });
            const contentMap = new Map();
            metaMap.outputContent.forEach((value, key) => {
                const entryPattern = this.getComponentPagePattern(key);
                contentMap.set(entryPattern.outputFiles.content, value);
            });
            metaMap.style = styleMap;
            const config = new Map();
            metaMap.useComponentPath.forEach((value, key) => {
                const entryPattern = this.getComponentPagePattern(key);
                const list = [
                    ...value.libraryPath.map((item) => {
                        item.path = (0, core_1.resolve)((0, core_1.normalize)('/'), (0, core_1.join)((0, core_1.normalize)(library_1.LIBRARY_OUTPUT_ROOTDIR), item.path));
                        return item;
                    }),
                ];
                list.push(...value.localPath.map((item) => ({
                    selector: item.selector,
                    path: (0, core_1.resolve)((0, core_1.normalize)('/'), (0, core_1.normalize)(this.getComponentPagePattern(item.path).outputFiles.path)),
                    className: item.className,
                })));
                config.set(entryPattern.outputFiles.config, {
                    component: entryPattern.type === 'component' || undefined,
                    usingComponents: list,
                    existConfig: entryPattern.inputFiles.config,
                });
            });
            for (const key in metaMap.otherMetaCollectionGroup) {
                if (Object.prototype.hasOwnProperty.call(metaMap.otherMetaCollectionGroup, key)) {
                    const element = metaMap.otherMetaCollectionGroup[key];
                    element.libraryPath.forEach((item) => {
                        item.path = (0, core_1.resolve)((0, core_1.normalize)('/'), (0, core_1.join)((0, core_1.normalize)(library_1.LIBRARY_OUTPUT_ROOTDIR), item.path));
                    });
                    element.localPath.forEach((item) => {
                        item.path = (0, core_1.resolve)((0, core_1.normalize)('/'), (0, core_1.normalize)(this.getComponentPagePattern(item.path).outputFiles.path));
                    });
                }
            }
            return {
                style: styleMap,
                outputContent: contentMap,
                config: config,
                otherMetaCollectionGroup: metaMap.otherMetaCollectionGroup,
                selfTemplate,
            };
        });
    }
    initHost(config) {
        const host = typescript_1.default.createIncrementalCompilerHost(config.options, this.system);
        this.augmentResolveModuleNames(host, config.options);
        this.addCleanDependency(host);
        return host;
    }
    initTscProgram() {
        return __awaiter(this, void 0, void 0, function* () {
            const { readConfiguration, NgtscProgram } = yield load_esm_1.angularCompilerCliPromise;
            const config = readConfiguration(this.tsConfig, undefined);
            const host = this.initHost(config);
            this.ngTscProgram = new NgtscProgram(config.rootNames, config.options, host);
            this.tsProgram = this.ngTscProgram.getTsProgram();
            this.typeChecker = this.tsProgram.getTypeChecker();
            this.augmentProgramWithVersioning(this.tsProgram);
            if (this.compiler.watchMode) {
                this.builder = this.oldBuilder =
                    typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram(this.tsProgram, host, this.oldBuilder);
            }
            else {
                this.builder = typescript_1.default.createAbstractBuilder(this.tsProgram, host);
            }
            this.ngCompiler = this.ngTscProgram.compiler;
        });
    }
    /** 获得组件/页面的入口 */
    getComponentPagePattern(fileName) {
        var _a;
        const findList = [fileName];
        let maybeEntryPath;
        while (findList.length) {
            const module = findList.shift();
            const moduleList = this.dependencyUseModule.get(path.normalize(module));
            if (moduleList && moduleList.length) {
                findList.push(...moduleList);
            }
            else {
                maybeEntryPath = this.pagePatternList.find((item) => path.normalize(item.src) === path.normalize(module));
                if (maybeEntryPath) {
                    const sourceFile = this.tsProgram.getSourceFile(maybeEntryPath.src);
                    const selector = (0, cyia_code_util_1.createCssSelectorForTs)(sourceFile);
                    let importComponent;
                    if (maybeEntryPath.type === 'page') {
                        const node = selector.queryOne(`CallExpression[expression=pageStartup]`);
                        importComponent = node.arguments[1];
                    }
                    else {
                        const node = selector.queryOne(`CallExpression[expression=componentRegistry]`);
                        importComponent = node.arguments[0];
                    }
                    const symbol = this.typeChecker.getSymbolAtLocation(importComponent);
                    const node = (_a = symbol === null || symbol === void 0 ? void 0 : symbol.getDeclarations()) === null || _a === void 0 ? void 0 : _a[0];
                    const importDeclaration = node === null || node === void 0 ? void 0 : node.parent.parent.parent;
                    const relativeImportComponentPath = importDeclaration.moduleSpecifier
                        .getText()
                        .slice(1, -1);
                    const importComponentPath = path.resolve(path.dirname(maybeEntryPath.src), path.normalize(relativeImportComponentPath)) + '.ts';
                    if (importComponentPath === path.normalize(fileName)) {
                        break;
                    }
                    maybeEntryPath = undefined;
                }
            }
        }
        if (!maybeEntryPath) {
            throw new Error(`没有找到组件[${fileName}]对应的入口点`);
        }
        return maybeEntryPath;
    }
    addCleanDependency(host) {
        const oldReadFile = host.readFile;
        const _this = this;
        host.readFile = function (fileName) {
            if (fileName.includes('node_modules')) {
                _this.cleanDependencyFileCacheSet.add((0, paths_1.externalizePath)(fileName));
            }
            return oldReadFile.call(this, fileName);
        };
    }
    saveModuleDependency(filePath, moduleName, module) {
        if (!module) {
            throw new Error(`模块未被解析,文件名${filePath},模块名${moduleName}`);
        }
        const useList = this.dependencyUseModule.get(path.normalize(module.resolvedFileName)) ||
            [];
        useList.push(filePath);
        this.dependencyUseModule.set(path.normalize(module.resolvedFileName), useList);
    }
    augmentResolveModuleNames(host, compilerOptions) {
        const moduleResolutionCache = typescript_1.default.createModuleResolutionCache(host.getCurrentDirectory(), host.getCanonicalFileName.bind(host), compilerOptions);
        const oldResolveModuleNames = host.resolveModuleNames;
        if (oldResolveModuleNames) {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            host.resolveModuleNames = (moduleNames, ...args) => {
                return moduleNames.map((name) => {
                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
                    const result = oldResolveModuleNames.call(host, [name], ...args);
                    this.saveModuleDependency(args[0], name, result);
                    return result;
                });
            };
        }
        else {
            host.resolveModuleNames = (moduleNames, containingFile, _reusedNames, redirectedReference, options) => {
                return moduleNames.map((name) => {
                    const result = typescript_1.default.resolveModuleName(name, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule;
                    if (!containingFile.includes('node_modules')) {
                        this.saveModuleDependency(containingFile, name, result);
                    }
                    return result;
                });
            };
        }
    }
    analyzeAsync() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.initTscProgram();
            yield this.ngCompiler.analyzeAsync();
        });
    }
    getBuilder() {
        return this.builder;
    }
    cleanDependencyFileCache() {
        this.cleanDependencyFileCacheSet.forEach((filePath) => {
            try {
                this.compiler.inputFileSystem.purge(filePath);
            }
            catch (error) { }
        });
    }
    augmentProgramWithVersioning(program) {
        const baseGetSourceFiles = program.getSourceFiles;
        program.getSourceFiles = function (...parameters) {
            const files = baseGetSourceFiles(...parameters);
            for (const file of files) {
                if (file.version === undefined) {
                    file.version = (0, crypto_1.createHash)('sha256').update(file.text).digest('hex');
                }
            }
            return files;
        };
    }
}
MiniProgramApplicationAnalysisService.ɵfac = function MiniProgramApplicationAnalysisService_Factory(t) { return new (t || MiniProgramApplicationAnalysisService)(i0.ɵɵinject(i0.Injector), i0.ɵɵinject(token_1.WEBPACK_COMPILATION), i0.ɵɵinject(token_1.TS_SYSTEM), i0.ɵɵinject(token_1.WEBPACK_COMPILER), i0.ɵɵinject(token_1.TS_CONFIG_TOKEN), i0.ɵɵinject(token_1.OLD_BUILDER), i0.ɵɵinject(token_1.PAGE_PATTERN_TOKEN), i0.ɵɵinject(i1.BuildPlatform)); };
MiniProgramApplicationAnalysisService.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MiniProgramApplicationAnalysisService, factory: MiniProgramApplicationAnalysisService.ɵfac });
exports.MiniProgramApplicationAnalysisService = MiniProgramApplicationAnalysisService;
