"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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.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.LegacyExternals = void 0;
const path = __importStar(require("path"));
const spfx_heft_plugins_1 = require("@microsoft/spfx-heft-plugins");
const node_core_library_1 = require("@rushstack/node-core-library");
const glob_1 = __importDefault(require("glob"));
const lodash_1 = require("lodash");
const LOCALE_TOKEN = '{locale}';
const ESCAPED_TOKEN_REGEXP = new RegExp(lodash_1.escapeRegExp(lodash_1.escapeRegExp(LOCALE_TOKEN)));
const ESCAPED_TOKEN_GLOBAL_REGEXP = new RegExp(lodash_1.escapeRegExp(lodash_1.escapeRegExp(LOCALE_TOKEN)), 'g');
const LOCALE_TOKEN_REGEXP = new RegExp(LOCALE_TOKEN, 'g');
class LegacyExternals {
    constructor(options) {
        this._legacyExternalsByName = new Map();
        this._gulpTask = options.gulpTask;
        this._singleLocale = options.singleLocale;
        this._configJson = options.configJson;
        this._buildFolder = options.buildFolder;
        this._serveMode = options.serveMode;
    }
    updateWebpackConfiguration(webpackConfiguration) {
        webpackConfiguration.externals = [
            ...(webpackConfiguration.externals || []),
            ...Object.keys(this._configJson.localizedResources || {}),
            ...Object.keys(this._configJson.externals || {})
        ];
        let foundManifestPlugin = false;
        for (const plugin of webpackConfiguration.plugins) {
            if (plugin instanceof spfx_heft_plugins_1._ManifestPlugin) {
                plugin._options.tryGetLegacyExternalByNameAsync = async (name) => {
                    return await this._tryGetLegacyExternalsByName(name);
                };
                foundManifestPlugin = true;
            }
        }
        if (!foundManifestPlugin) {
            throw new Error('Unable to find SPFx Manifest Plugin');
        }
    }
    async _ensureLocalizedResourcesAsync() {
        if (!this._discoveredLocalizedResources) {
            const localizedResourceKeys = Object.keys(this._configJson.localizedResources || {});
            this._gulpTask.logVerbose(`${localizedResourceKeys.length} localized resources defined.`);
            this._discoveredLocalizedResources = new Map();
            for (const [resourceId, pattern] of Object.entries(this._configJson.localizedResources)) {
                let result = await this._collectResourcesForKeyAsync(pattern);
                if (this._singleLocale) {
                    const singleLocaleData = result[this._singleLocale];
                    if (!singleLocaleData) {
                        throw new Error(`The selected locale (${this._singleLocale}) is not present in the localized data for resource ` +
                            `"${resourceId}".`);
                    }
                    else {
                        result = {
                            [this._singleLocale]: singleLocaleData
                        };
                    }
                }
                this._discoveredLocalizedResources.set(resourceId, result);
            }
        }
        return this._discoveredLocalizedResources;
    }
    async _collectResourcesForKeyAsync(rawPattern) {
        // If the pattern doesn't have a leading slash, include one so the glob pattern resolves to under the root directory
        const rawPatternMatches = rawPattern.match(/(?:^\/+)(.+)/);
        const pattern = rawPatternMatches ? rawPatternMatches[1] : rawPattern;
        if (rawPatternMatches) {
            this._gulpTask.logWarning(`The localized file pattern "${rawPattern}" contains a leading slash. These patterns resolve ` +
                `to paths under the root directory ("${this._buildFolder}"), so they are intended to ` +
                'be relative. A leading slash is not recommended and may produce unexpected behavior.');
        }
        if (!pattern.match(LOCALE_TOKEN_REGEXP)) {
            // Singleton locale, no pattern token
            const fullPath = path.join(this._buildFolder, pattern);
            if (await node_core_library_1.FileSystem.existsAsync(fullPath)) {
                // Singleton found, resolve as the default locale
                return {
                    default: fullPath
                };
            }
            else {
                // Singleton not found, throw an error
                throw new Error(`Singleton resource "${rawPattern}" not found.`);
            }
        }
        else {
            const globPattern = pattern.replace(LOCALE_TOKEN_REGEXP, '*');
            const files = await node_core_library_1.LegacyAdapters.convertCallbackToPromise(glob_1.default, globPattern, {
                cwd: this._buildFolder
            });
            if (!files || files.length === 0) {
                throw new Error(`No localized files found under the root directory ("${this._buildFolder}") matching ` +
                    `pattern "${rawPattern}"`);
            }
            else {
                // This turns a pattern that looks something like this: "asdf\{locale}/{locale}/{locale}.js"
                // into a regex that looks something like this: /asdf\/(.+)\/(?:\\1)\/(?:\\1).js/
                const normalizedPattern = path.posix.resolve('/', pattern).substr(1);
                const regexPattern = new RegExp(lodash_1.escapeRegExp(normalizedPattern)
                    .replace(ESCAPED_TOKEN_REGEXP, '(.+)')
                    // this lint rule actually has a bug, so we'll remove this exception when it gets fixed. VSO#513580
                    // tslint:disable-next-line:no-octal-literal
                    .replace(ESCAPED_TOKEN_GLOBAL_REGEXP, '(?:\\1)'));
                const result = {};
                for (const file of files) {
                    const posixFile = file.replace(/\\/g, '/');
                    const match = posixFile.match(regexPattern);
                    if (!match) {
                        this._gulpTask.logWarning(`Unexpected file path: "${file}"`);
                    }
                    else {
                        const locale = match[1];
                        this._gulpTask.logVerbose(`Found file "${file}", identified as locale "${locale}"`);
                        result[locale] = path.join(this._buildFolder, file);
                    }
                }
                return result;
            }
        }
    }
    async _tryGetLegacyExternalsByName(externalName) {
        let result = this._legacyExternalsByName.get(externalName);
        if (!result) {
            const discoveredLocalizedResources = await this._ensureLocalizedResourcesAsync();
            const localizedFiles = discoveredLocalizedResources.get(externalName);
            if (localizedFiles) {
                const manifestModulePaths = {};
                const assets = {};
                let localesProcessed = 0;
                let singleLocaleName = undefined;
                for (const [localeName, filePath] of Object.entries(localizedFiles)) {
                    const fileContents = await node_core_library_1.FileSystem.readFileAsync(filePath);
                    const assetFilenameParts = [`${externalName}_${localeName}`];
                    if (!this._serveMode) {
                        const fileHash = spfx_heft_plugins_1._getFullHash(fileContents);
                        assetFilenameParts.push(`_${fileHash}`);
                    }
                    assetFilenameParts.push(path.extname(filePath));
                    const assetFilename = assetFilenameParts.join('');
                    const normalizedLocaleName = spfx_heft_plugins_1._normalizeLocaleName(localeName);
                    manifestModulePaths[normalizedLocaleName] = assetFilename;
                    assets[assetFilename] = fileContents;
                    localesProcessed++;
                    if (localesProcessed === 1) {
                        singleLocaleName = normalizedLocaleName;
                    }
                    else {
                        singleLocaleName = undefined;
                    }
                }
                let manifestModule;
                if (singleLocaleName) {
                    manifestModule = {
                        type: 'path',
                        path: manifestModulePaths[singleLocaleName]
                    };
                }
                else {
                    manifestModule = {
                        type: 'localizedPath',
                        paths: manifestModulePaths,
                        defaultPath: spfx_heft_plugins_1._LocalizedManifestModuleProcessor.findDefaultString(manifestModulePaths)
                    };
                }
                result = {
                    moduleConfigurations: { [externalName]: manifestModule },
                    assets: assets
                };
            }
            else if (this._configJson.externals && this._configJson.externals[externalName]) {
                const external = this._configJson.externals[externalName];
                result = {
                    moduleConfigurations: {},
                    assets: {}
                };
                const includeExternal = async (name, externalModule) => {
                    let externalPath;
                    let globalExportName;
                    if (typeof externalModule === 'string') {
                        externalPath = externalModule;
                    }
                    else {
                        externalPath = externalModule.path;
                        globalExportName = externalModule.globalName;
                        if (externalModule.globalDependencies) {
                            await Promise.all(externalModule.globalDependencies.map(async (dependencyName) => {
                                const dependency = this._configJson
                                    .externals[dependencyName];
                                if (!dependency) {
                                    throw new Error(`Dependency "${dependencyName}" of external "${name}" is not defined. ` +
                                        'It must be defined as an external and it must not be a SPFx component.');
                                }
                                await includeExternal(dependencyName, dependency);
                            }));
                        }
                    }
                    let assetLoadPath;
                    if (/^https?\:/.test(externalPath)) {
                        assetLoadPath = externalPath;
                    }
                    else {
                        let fileContents;
                        const externalFullPath = path.join(this._buildFolder, externalPath);
                        try {
                            fileContents = await node_core_library_1.FileSystem.readFileAsync(externalPath);
                        }
                        catch (e) {
                            if (node_core_library_1.FileSystem.isNotExistError(e)) {
                                throw new Error(`"${externalFullPath}" does not exist. Ensure the path is correct and relative to ` +
                                    'the project root.');
                            }
                            else {
                                throw e;
                            }
                        }
                        const assetFilenameParts = [externalName];
                        if (!this._serveMode) {
                            const fileHash = spfx_heft_plugins_1._getFullHash(fileContents);
                            assetFilenameParts.push(`_${fileHash}`);
                        }
                        assetFilenameParts.push(path.extname(externalPath));
                        const assetFilename = assetFilenameParts.join('');
                        assetLoadPath = assetFilename;
                        result.assets[assetFilename] = fileContents;
                    }
                    result.moduleConfigurations[name] = {
                        type: 'path',
                        path: assetLoadPath,
                        globalName: globalExportName
                    };
                };
                await includeExternal(externalName, external);
            }
            if (result) {
                this._legacyExternalsByName.set(externalName, result);
            }
        }
        return result;
    }
}
exports.LegacyExternals = LegacyExternals;
//# sourceMappingURL=LegacyExternals.js.map