"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 });
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = require("path");
const lodash_1 = require("lodash");
const execa_1 = __importDefault(require("execa"));
const semver_1 = __importDefault(require("semver"));
const path_2 = __importDefault(require("path"));
const transpile_1 = require("./transpile/transpile");
const variable_transformer_1 = __importDefault(require("./variables/variable.transformer"));
const utils_1 = require("./utils");
const bind_ex_transformer_1 = __importDefault(require("./transpile/bind.ex.transformer"));
const theme_service_1 = require("./theme/theme.service");
const profile_1 = __importDefault(require("./profiles/profile"));
const app_override_css_generator_1 = require("./theme/app-override-css.generator");
const command_1 = require("../command");
const APP_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/app.template');
const APP_THEME_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/app.theme.template');
const BABEL_CONFIG_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/babel.config.js.template');
const BOOTSTRAP_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/bootstrap.template');
const RESOURCE_RESOLVER_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/resource.resolver.template');
const THEME_ASSET_RESOLVER_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/theme-asset.resolver.template');
const COMPONENT_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/component/component.template');
const COMPONENT_PROPS_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/component/component.props.template');
const SCRIPT_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/component/script.template');
const STYLE_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/component/style.template');
const PAGE_CONFIG_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/pages-config.template');
const SERVICE_DEFINITIONS = (0, utils_1.loadTemplate)(__dirname + '/templates/service.defs.template');
const FORMATTERS_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/extensions/formatters.template');
const VARIABLE_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/variables.template');
const DEVICE_SERVICES_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/device-operation-loader.template');
const ENTITY_PROVIDER_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/entity-provider.template');
const WMX_COMPONENT_NATIVE_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/wmxcomponent/wmxcomponent.native.template');
const WMX_COMPONENT_WEB_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/wmxcomponent/wmxcomponent.web.template');
const DEVICE_PLUGIN_SERVICES_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/device-plugin-service.template');
const DEVICE_PERMISSION_SERVICE_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/device-permission-service.template');
const PLUGIN_PROVIDER_TEMPLATE = (0, utils_1.loadTemplate)(__dirname + '/templates/plugin-provider.template');
const ALL_GENERATED_FILES = '(src/**/*.js|App.js|app.style.js|bootstrap.js)';
// REF: https://github.com/facebook/metro/blob/master/packages/metro-config/src/defaults/defaults.js#L14-L44
const ALLOWED_RESOURCE_EXT = [
    // Image formats
    'bmp', 'gif', 'jpg', 'jpeg', 'png', 'psd', 'svg', 'webp',
    // Video formats
    'm4v', 'mov', 'mp4', 'mpeg', 'mpg', 'webm',
    // Audio formats
    'aac', 'aiff', 'caf', 'm4a', 'mp3', 'wav',
    // Document formats
    'html', 'pdf', 'yaml', 'yml', 'json',
    // Font formats
    'otf', 'ttf',
    // Archives (virtual files)
    'zip',
    'js'
];
const pluginOperationConfig = [];
const operationMap = {
    'captureImage': { name: 'Camera', method: 'CaptureImageOperation', filename: 'capture-image', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system', 'expo-av'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
    'captureVideo': { name: 'Camera', method: 'CaptureVideoOperation', filename: 'capture-video', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system', 'expo-av'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
    'scanBarCode': { name: 'Scan', method: 'ScanOperation', filename: 'scan', hasConstructorParams: true, packages: ['expo-camera'], tagName: ['WmBarcodescanner'], removePlugin: true },
    'getAppInfo': { name: 'Device', method: 'AppInfoOperation', filename: 'app-info', hasConstructorParams: false, packages: [], tagName: [], removePlugin: false },
    'getDeviceInfo': { name: 'Device', method: 'DeviceInfoOperation', filename: 'device-info', hasConstructorParams: false, packages: ['expo-device'], tagName: [], removePlugin: false },
    'getNetworkInfo': { name: 'Device', method: 'NetworkInfoOperation', filename: 'network-info', hasConstructorParams: false, packages: ['expo-network', '@react-native-community/netinfo'], removePlugin: false },
    'vibrate': { name: 'Device', method: 'VibrateOperation', filename: 'vibrate', hasConstructorParams: false, packages: [], tagName: [], removePlugin: false },
    'getCurrentGeoPosition': { name: 'Location', method: 'CurrentGeoPositionOperation', filename: 'current-geo-position', hasConstructorParams: true, packages: ['expo-location'], tagName: [], removePlugin: true },
    'getContacts': { name: 'Contacts', method: 'GetContactsOperation', filename: 'get-contacts', hasConstructorParams: true, packages: ['expo-contacts'], tagName: [], removePlugin: true },
    'getEvents': { name: 'Calendar', method: 'GetEventsOperation', filename: 'get-events', hasConstructorParams: true, packages: ['expo-calendar'], tagName: [], removePlugin: true },
    'createEvent': { name: 'Calendar', method: 'CreateEventOperation', filename: 'create-event', hasConstructorParams: true, packages: ['expo-calendar'], tagName: [], removePlugin: true },
    'deleteEvent': { name: 'Calendar', method: 'DeleteEventOperation', filename: 'delete-event', hasConstructorParams: true, packages: ['expo-calendar'], tagName: [], removePlugin: true },
    'upload': { name: 'File', method: 'UploadFileOperation', filename: 'upload-file', hasConstructorParams: false, packages: ['expo-document-picker'], tagName: ['WmFileupload'], removePlugin: true },
    'playAudio': { name: 'Audio', method: 'PlayAudioOperation', filename: '', hasConstructorParams: false, packages: ['expo-av'], tagName: ['WmAudio'], removePlugin: true },
    'playVideo': { name: 'Video', method: 'PlayVideoOperation', filename: '', hasConstructorParams: false, packages: ['expo-video'], tagName: ['WmVideo'], removePlugin: true },
    'openFile': { name: 'File', method: 'OpenFileOperation', filename: 'open-file', hasConstructorParams: false, packages: ['expo-sharing', 'expo-file-system'], tagName: [], removePlugin: true },
};
const serviceParams = {
    'Camera': [{
            name: 'AppDisplayManagerService',
            filename: 'app-display-manager.service'
        }],
    'Scan': [{
            name: 'AppDisplayManagerService',
            filename: 'app-display-manager.service'
        }],
    'Location': [],
    'Contacts': [],
    'Calendar': []
};
let deviceVariables = [];
let appVersion;
const servicesConfig = [];
class AppGenerator {
    constructor(projectPath, projectService, appUrl, prefabName = '', incBuilder, options, dynamicPrefabProps) {
        this.projectPath = projectPath;
        this.projectService = projectService;
        this.appUrl = appUrl;
        this.prefabName = prefabName;
        this.incBuilder = incBuilder;
        this.options = options;
        this.dynamicPrefabProps = dynamicPrefabProps;
        this.transpiledComponents = {
            pages: {},
            partials: {},
            prefabs: {},
            wmxComponents: {}
        };
        this.liveVariables = [];
        this.isPrefabApp = false;
        this.rnConfig = {};
        this.themeService = new theme_service_1.ThemeService();
        this.projectPackageDependencies = Object.values(operationMap).map((i) => i);
        this.pluginsInApp = {};
        this.wmxComponents = [];
        this.jsFileImports = new Set();
        this.shouldNpmInstall = false;
        this.shouldDoUnwantedPackagesCleanup = false;
        this.options = options;
        this.isPrefabApp = !!prefabName;
        this.wmxComponents = this.projectService.getWMXComponents();
    }
    prepareVariables(variableJSON, scope, imports) {
        const variables = Object.values(variableJSON)
            .map((v) => (0, variable_transformer_1.default)(v, scope, this.appUrl)).filter(v => !!v);
        return VARIABLE_TEMPLATE({
            scope: scope,
            imports: imports,
            addEntityProvider: !this.isPrefabApp && scope !== 'App',
            isPrefabApp: this.isPrefabApp,
            prefabName: this.prefabName,
            context: {
                Variables: variables.filter(v => v.group === 'variable'),
                Actions: variables.filter(v => v.group === 'action')
            }
        });
    }
    getFilePath(name) {
        let nameArr = name.split('');
        const index = nameArr.findIndex(character => {
            return (character === character.toUpperCase() && character !== '-');
        });
        if (index === -1) {
            return name;
        }
        if (index === 0) {
            name = name[index].toLowerCase() + name.substring(index + 1);
        }
        else {
            name = name.substring(0, index) + '-' + name[index].toLowerCase() + name.substring(index + 1);
        }
        return this.getFilePath(name);
    }
    /**
     * @description Get the list of packages (from operationMap) that are used in the generated app
     * @returns packagesInAppObj: Record<string, boolean>
     */
    async getListOfPackagesUsedInGeneratedApp() {
        var _a, _b;
        const operations = (0, lodash_1.uniq)((_a = this.projectPackageDependencies) === null || _a === void 0 ? void 0 : _a.map((p) => p.name));
        const packagesInAppObj = {};
        // common device operations present in all WaveMaker apps (e.g, getAppInfo, getDeviceInfo, getNetworkInfo)
        const commonPluginsUsedInApp = ((_b = this.projectPackageDependencies.filter((p) => !p.removePlugin)) === null || _b === void 0 ? void 0 : _b.length) > 0;
        /**
         * find all operations/packages (e.g, Camera, Scan, Location)
         * that are used in the app
         */
        await Promise.all(operations.map(async (operation) => {
            let method = "";
            const importedWidgets = await this.getWidgetImports();
            const deviceVariables = await this.getDeviceVariables();
            const isWidgetImportedForCurrentOperation = importedWidgets.findIndex((w) => {
                const widgetName = operation === "File"
                    ? "WmFileupload"
                    : operation === "Scan"
                        ? "WmBarcodescanner"
                        : `Wm${operation}`;
                return w.name === widgetName;
            }) !== -1;
            // other device operations used in the app
            // that can be removed if unused (e.g, captureImage, scanBarCode)
            const deviceOperationsUsedInApp = deviceVariables === null || deviceVariables === void 0 ? void 0 : deviceVariables.filter((config) => {
                if (config.type === operation) {
                    method = config.method;
                }
                return config.type === operation;
            });
            // operation is used if it's widget import is found or the device operation is found
            const isCurrentOperationUsedInApp = isWidgetImportedForCurrentOperation || deviceOperationsUsedInApp.length > 0;
            switch (operation) {
                case 'Device':
                    packagesInAppObj['hasDevicePlugin'] = commonPluginsUsedInApp;
                    break;
                case 'Camera':
                case 'Scan':
                case 'Location':
                case 'Contacts':
                case 'Calendar':
                case 'Audio':
                case 'Video':
                    packagesInAppObj[`has${operation}Plugin`] = isCurrentOperationUsedInApp;
                    break;
                case 'File':
                    if (deviceOperationsUsedInApp.length >= 2) {
                        packagesInAppObj['hasUploadFilePlugin'] = true;
                        packagesInAppObj['hasOpenFilePlugin'] = true;
                    }
                    else {
                        if (method === 'UploadFileOperation' || (operation === 'File' && isWidgetImportedForCurrentOperation)) {
                            packagesInAppObj['hasUploadFilePlugin'] = true;
                        }
                        else if (method === 'OpenFileOperation') {
                            packagesInAppObj['hasOpenFilePlugin'] = true;
                        }
                    }
                    break;
                default:
                    break;
            }
        }));
        return packagesInAppObj;
    }
    async generatePluginsOperationConfig() {
        const servicesList = [];
        // * get all device variables (both app and page level) before processing
        let allDeviceVariables = await this.getDeviceVariables();
        allDeviceVariables.forEach((v) => {
            const hasEntry = pluginOperationConfig.find(p => p.operation === v.operation);
            if (hasEntry) {
                return false;
            }
            const selectedOperation = operationMap[v.operation];
            if (!selectedOperation) {
                console.log('Cannot find operation named ' + v.operation);
            }
            const excludeServices = ['Device', 'File'];
            if (!servicesList.includes(selectedOperation.name) && !excludeServices.includes(selectedOperation.name)) {
                servicesList.push(selectedOperation.name);
            }
            pluginOperationConfig.push({
                'operation': v.operation,
                'service': v.service,
                'filename': selectedOperation.filename,
                'method': selectedOperation.method,
                'hasparams': selectedOperation.hasConstructorParams,
                'type': selectedOperation.name
            });
        });
        let uniqueImports = [];
        servicesList.forEach((s) => {
            const item = serviceParams[s] || [];
            if (item && item.length) {
                item.forEach((e) => {
                    var itemName = e.name;
                    const isImported = uniqueImports.length && uniqueImports.find(i => {
                        return i.name === itemName;
                    });
                    if (!isImported) {
                        uniqueImports.push(e);
                    }
                });
            }
            servicesConfig.push({
                name: s,
                constructorParams: item,
                servicesStr: '',
                filePath: ''
            });
        });
        servicesConfig.forEach((item) => {
            let servicesStr = '';
            item.constructorParams.forEach((o) => {
                const serviceName = o['name'];
                if (serviceName) {
                    if (!servicesStr) {
                        servicesStr = servicesStr + serviceName;
                    }
                    if (!servicesStr.includes(serviceName)) {
                        servicesStr = servicesStr + ', ' + serviceName;
                    }
                }
            });
            item.servicesStr = servicesStr;
            item.filePath = this.getFilePath(item.name);
        });
        const buildCachePath = `${this.projectPath}/.build-cache`;
        if (fs_extra_1.default.existsSync(buildCachePath)) {
            const buildCache = fs_extra_1.default.readJSONSync(buildCachePath);
            if (!buildCache.deviceVariables) {
                buildCache.deviceVariables = [];
            }
            // * Write deviceVariables to build-cache if it's changed compared to previous build
            const excludeVariables = ['Device'];
            const filteredDeviceVariables = pluginOperationConfig.filter((v) => !excludeVariables.includes(v.type)).map((v) => v.method);
            if (!(0, lodash_1.isEqual)(buildCache.deviceVariables, filteredDeviceVariables)) {
                // console.log('<== Project device variables changed ==>');
                buildCache.deviceVariables = filteredDeviceVariables;
                this.shouldDoUnwantedPackagesCleanup = true;
                fs_extra_1.default.writeJSONSync(buildCachePath, buildCache);
            }
            else if (this.incBuilder) {
                // * If device variables are the same, return early
                return;
            }
        }
        else {
            fs_extra_1.default.writeJSONSync(buildCachePath, {});
        }
        // * get plugin list before generating device operation loader
        this.pluginsInApp = await this.getListOfPackagesUsedInGeneratedApp();
        // previously pluginOperationConfig was only having device variables from app.variables.js, not from Page level variables (e.g, Main.variables.js)
        const output = DEVICE_SERVICES_TEMPLATE({
            pluginOperationConfig: pluginOperationConfig,
            appVersion: appVersion,
            servicesConfig: servicesConfig,
            imports: uniqueImports,
            expoCameraPlugin: this.pluginsInApp.hasCameraPlugin,
            scanPlugin: this.pluginsInApp.hasScanPlugin,
            locationPlugin: this.pluginsInApp.hasLocationPlugin,
            contactsPlugin: this.pluginsInApp.hasContactsPlugin,
            calendarPlugin: this.pluginsInApp.hasCalendarPlugin,
            fileUploadPlugin: this.pluginsInApp.hasUploadFilePlugin,
            audioPlugin: this.pluginsInApp.hasAudioPlugin,
            videoPlugin: this.pluginsInApp.hasVideoPlugin,
            openFilePlugin: this.pluginsInApp.hasOpenFilePlugin
        });
        (0, utils_1.writeFile)(`${this.projectPath}/src/device-operation-loader.js`, output);
        // * generate device plugin services, permission service & plugin provider
        const pluginServices = DEVICE_PLUGIN_SERVICES_TEMPLATE({
            expoCameraPlugin: this.pluginsInApp.hasCameraPlugin,
            scanPlugin: this.pluginsInApp.hasScanPlugin,
            locationPlugin: this.pluginsInApp.hasLocationPlugin,
            contactsPlugin: this.pluginsInApp.hasContactsPlugin,
            calendarPlugin: this.pluginsInApp.hasCalendarPlugin,
            fileUploadPlugin: this.pluginsInApp.hasUploadFilePlugin,
            audioPlugin: this.pluginsInApp.hasAudioPlugin,
            videoPlugin: this.pluginsInApp.hasVideoPlugin,
            openFilePlugin: this.pluginsInApp.hasOpenFilePlugin
        });
        const permissionService = DEVICE_PERMISSION_SERVICE_TEMPLATE({
            expoCameraPlugin: this.pluginsInApp.hasCameraPlugin,
            scanPlugin: this.pluginsInApp.hasScanPlugin,
            locationPlugin: this.pluginsInApp.hasLocationPlugin,
            contactsPlugin: this.pluginsInApp.hasContactsPlugin,
            calendarPlugin: this.pluginsInApp.hasCalendarPlugin,
            fileUploadPlugin: this.pluginsInApp.hasUploadFilePlugin,
            audioPlugin: this.pluginsInApp.hasAudioPlugin,
            videoPlugin: this.pluginsInApp.hasVideoPlugin,
            openFilePlugin: this.pluginsInApp.hasOpenFilePlugin
        });
        const pluginProvider = PLUGIN_PROVIDER_TEMPLATE({
            expoCameraPlugin: this.pluginsInApp.hasCameraPlugin,
            scanPlugin: this.pluginsInApp.hasScanPlugin,
            locationPlugin: this.pluginsInApp.hasLocationPlugin,
            contactsPlugin: this.pluginsInApp.hasContactsPlugin,
            calendarPlugin: this.pluginsInApp.hasCalendarPlugin,
            fileUploadPlugin: this.pluginsInApp.hasUploadFilePlugin,
            audioPlugin: this.pluginsInApp.hasAudioPlugin,
            videoPlugin: this.pluginsInApp.hasVideoPlugin,
            openFilePlugin: this.pluginsInApp.hasOpenFilePlugin
        });
        (0, utils_1.writeFile)(`${this.projectPath}/src/device-plugin-service.js`, pluginServices);
        (0, utils_1.writeFile)(`${this.projectPath}/src/device-permission-service.js`, permissionService);
        (0, utils_1.writeFile)(`${this.projectPath}/src/plugin-provider.js`, pluginProvider);
    }
    async prepareDeviceOperationLoader() {
        const config = fs_extra_1.default.readJsonSync(`${this.projectPath}/wm_rn_config.json`, {
            encoding: 'utf8'
        });
        appVersion = config.version;
        await this.generatePluginsOperationConfig();
    }
    isPartial(type) {
        return type === 'PARTIAL' || type === 'LEFTNAV' || type === 'POPOVER' || type === 'TEMPLATE';
    }
    prettify(target = ALL_GENERATED_FILES) {
        let prettierPath = null;
        let d = __dirname;
        while (d) {
            prettierPath = d + path_1.sep + 'node_modules' + path_1.sep + 'prettier';
            if (fs_extra_1.default.existsSync(prettierPath)) {
                break;
            }
            const s = d.split(path_1.sep);
            s.pop();
            d = s.join(path_1.sep);
        }
        if (d) {
            return (0, execa_1.default)('node', [`${prettierPath}${path_1.sep}bin-prettier.js`, '--write', `${this.projectPath}/${target}`]);
        }
        console.warn('Prettier is not found');
        return Promise.reject();
    }
    setDeviceVariables(variables) {
        variables = Object.values(variables);
        const filterOutput = variables.filter((v) => v.category === 'wm.DeviceVariable');
        deviceVariables = deviceVariables.concat(filterOutput);
    }
    savePageDesignStyles(designStyles, pageName) {
        const p = `${this.projectPath}/designtime/pages/${pageName}`;
        fs_extra_1.default.mkdirpSync(p);
        fs_extra_1.default.writeFileSync(`${p}/${pageName}.css`, designStyles);
        return Promise.resolve();
    }
    saveAppDesignStyles(designStyles) {
        const p = `${this.projectPath}/designtime`;
        fs_extra_1.default.mkdirpSync(p);
        fs_extra_1.default.writeFileSync(`${p}/app.css`, designStyles);
        return Promise.resolve();
    }
    generateAppVariables() {
        if (this.incBuilder) {
            console.log(`Generating App variables.`);
        }
        return this.projectService.getAppVariables().then(variables => {
            this.setDeviceVariables(variables);
            Object.keys(variables).map((key) => {
                // @ts-ignore
                if (variables[key]["category"] === 'wm.LiveVariable') {
                    // @ts-ignore
                    this.liveVariables.push(variables[key]);
                }
            });
            const output = this.prepareVariables(variables, 'App', []);
            (0, utils_1.writeFile)(`${this.projectPath}/src/app.variables.js`, output);
        });
    }
    generateRNConfig() {
        if (this.incBuilder) {
            console.log(`Generating RN config.`);
        }
        return this.projectService.getRNConfig().then(data => {
            var _a;
            const config = JSON.parse(data);
            if (profile_1.default.generateWeb) {
                config.serverPath = '..';
            }
            else if ((_a = config.serverPath) === null || _a === void 0 ? void 0 : _a.startsWith('http://NOSERVERREQUIRED.com')) {
                config.serverPath = null;
            }
            if ((0, lodash_1.isUndefined)(config.preferences.enableLogs) && profile_1.default.targetPlatform === 'web') {
                config.preferences.enableLogs = true;
            }
            config.preferences['errorImage'] = "resources/assets/error-icon.png";
            config.loader = config.loader ? config.loader : 'skeleton';
            (0, utils_1.writeFile)(`${this.projectPath}/wm_rn_config.json`, JSON.stringify(config, null, 4));
        });
    }
    removeOldKeys(splash) {
        const keysToRemove = ['animationSrc', 'width', 'height', 'modifiedSrc'];
        const filterKeys = (obj) => Object.fromEntries(Object.entries(obj).filter(([key]) => !keysToRemove.includes(key)));
        const { dark, ...rest } = splash;
        return {
            ...filterKeys(rest),
            ...(dark && Object.keys(filterKeys(dark)).length > 0 && {
                dark: filterKeys(dark)
            })
        };
    }
    updateInSplashConfig(config, appConfig) {
        var _a, _b, _c;
        let filteredSplashConfig = this.removeOldKeys(config.splash);
        if (filteredSplashConfig.image) {
            filteredSplashConfig['image'] = './assets/' + filteredSplashConfig.image;
        }
        if ((_a = filteredSplashConfig === null || filteredSplashConfig === void 0 ? void 0 : filteredSplashConfig.dark) === null || _a === void 0 ? void 0 : _a.image) {
            filteredSplashConfig['dark']['image'] = './assets/' + ((_b = filteredSplashConfig === null || filteredSplashConfig === void 0 ? void 0 : filteredSplashConfig.dark) === null || _b === void 0 ? void 0 : _b.image);
        }
        // support for old projects with splash in src and bgColor
        if (filteredSplashConfig.src) {
            filteredSplashConfig['image'] = './assets/' + filteredSplashConfig.src;
            delete filteredSplashConfig.src;
        }
        if (filteredSplashConfig.src) {
            filteredSplashConfig['backgroundColor'] = filteredSplashConfig.bgColor;
            delete filteredSplashConfig.bgColor;
        }
        if (filteredSplashConfig.modifiedSrc) {
            delete filteredSplashConfig.modifiedSrc;
        }
        if ((_c = Object.keys(config.splash)) === null || _c === void 0 ? void 0 : _c.length) {
            appConfig['expo']['plugins'].forEach((val) => {
                if (val[0] === 'expo-splash-screen') {
                    val[1] = filteredSplashConfig;
                    delete appConfig.expo.splash;
                }
            });
        }
    }
    generateAppConfig() {
        if (this.incBuilder) {
            console.log(`Generating App config.`);
        }
        let appConfig = fs_extra_1.default.readJsonSync(`${this.projectPath}/app.json`, { encoding: 'utf8' });
        this.projectService.getRNConfig().then(data => {
            var _a;
            const config = JSON.parse(data);
            appConfig['expo']['name'] = config.name;
            appConfig['expo']['slug'] = config.name;
            appConfig['expo']['version'] = config.version;
            appConfig['expo']['android']['package'] = config.id;
            appConfig['expo']['ios']['bundleIdentifier'] = config.id;
            appConfig['expo']['jsEngine'] = ((_a = config.preferences) === null || _a === void 0 ? void 0 : _a.enableHermes) === 'false' ? 'jsc' : 'hermes';
            appConfig['expo']['icon'] = './assets/' + config.icon.src;
            appConfig['expo']['android']['adaptiveIcon']['foregroundImage'] = './assets/' + config.icon.src;
            appConfig['expo']['orientation'] = config.orientation || 'portrait';
            this.updateInSplashConfig(config, appConfig);
        }).then(() => {
            return this.projectService.getAppJSON()
                .then(appConfigOverride => {
                var _a, _b;
                (_b = (_a = appConfigOverride === null || appConfigOverride === void 0 ? void 0 : appConfigOverride.expo) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.forEach((plugin) => {
                    var _a, _b;
                    if ((_a = appConfig === null || appConfig === void 0 ? void 0 : appConfig.expo) === null || _a === void 0 ? void 0 : _a.plugins) {
                        const presentInIndex = appConfig.expo.plugins.findIndex((item) => item[0] === plugin[0]);
                        if (presentInIndex > -1 && Array.isArray(appConfig.expo.plugins[presentInIndex])) {
                            appConfig.expo.plugins[presentInIndex][1] = plugin[1];
                        }
                        else {
                            appConfig.expo.plugins.push(plugin);
                        }
                    }
                    else {
                        if (!((_b = appConfig['expo']) === null || _b === void 0 ? void 0 : _b.plugins)) {
                            appConfig['expo'].plugins = [];
                        }
                        appConfig.expo.plugins.push(plugin);
                    }
                });
                const pluginsCopy = JSON.parse(JSON.stringify(appConfig.expo.plugins));
                const mergedConfig = (0, lodash_1.merge)(appConfig, appConfigOverride);
                mergedConfig.expo.plugins = pluginsCopy;
                return mergedConfig;
            });
        }).then((newAppConfig) => {
            (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(newAppConfig, null, 4));
        });
    }
    generateAppScript() {
        if (this.incBuilder) {
            console.log(`Generating App.js.`);
        }
        return this.projectService.getRNConfig().then(data => {
            var _a, _b, _c;
            const config = JSON.parse(data);
            const { animationSrc, dark } = config.splash || {};
            const lightAnimatedSplashExists = animationSrc && fs_extra_1.default.existsSync(this.projectPath + '/assets/' + animationSrc);
            const darkAnimatedSplashExists = (dark === null || dark === void 0 ? void 0 : dark.animationSrc) && fs_extra_1.default.existsSync(this.projectPath + '/assets/' + dark.animationSrc);
            const animatedSplash = (profile_1.default.targetPlatform === 'native' && (lightAnimatedSplashExists || darkAnimatedSplashExists));
            const sslPinning = profile_1.default.targetPlatform === 'web'
                || !((_a = config.sslPinning) === null || _a === void 0 ? void 0 : _a.enabled)
                || (0, lodash_1.isEmpty)((_b = config.sslPinning) === null || _b === void 0 ? void 0 : _b.domains) ? null : (_c = config.sslPinning) === null || _c === void 0 ? void 0 : _c.domains;
            sslPinning && Object.values(sslPinning).forEach((v) => {
                if (v.publicKeyHashes.length === 1) {
                    v.publicKeyHashes.push("NoBackUpKey" + v.publicKeyHashes[0].substring(11));
                }
            });
            const enableWavePulse = (!!config.preferences.enableWavePulse || profile_1.default.targetPlatform === 'web');
            if (!enableWavePulse) {
                if (fs_extra_1.default.existsSync(`${this.projectPath}/wavepulse`)) {
                    fs_extra_1.default.rmSync(`${this.projectPath}/wavepulse`, {
                        recursive: true
                    });
                }
            }
            return this.projectService.getAppVariables().then(variables => {
                return this.projectService.getAppJs().then(script => {
                    var _a;
                    const supportedLocaleVariable = (_a = Object.values(variables).filter((v) => (v.startUpdate || v.category === 'wm.Variable') && v.name === 'supportedLocale')) === null || _a === void 0 ? void 0 : _a[0];
                    const localeDataset = Object.keys(supportedLocaleVariable === null || supportedLocaleVariable === void 0 ? void 0 : supportedLocaleVariable.dataSet);
                    const supportedLocales = localeDataset.filter(utils_1.isValidLocale);
                    const output = APP_TEMPLATE({
                        script: script,
                        startUpVariables: Object.values(variables).filter((v) => (v.startUpdate || v.category === 'wm.Variable') && v.category !== 'wm.TimerVariable').map((v) => v.name),
                        startUpActions: Object.values(variables).filter((v) => v.startUpdate && v.category === 'wm.TimerVariable').map((v) => v.name),
                        autoUpdateVariables: Object.values(variables).filter((v) => v.autoUpdate || ((v.category === 'wm.Variable' || v.category === 'wm.LiveVariable') && v.dataBinding && v.dataBinding.length)).map((v) => v.name),
                        supportedLocales: supportedLocales,
                        animatedSplash: animatedSplash,
                        sslPinning: sslPinning,
                        enableWavePulse: enableWavePulse,
                        enableDesignTokens: !!config.preferences.enableDesignTokens
                    });
                    (0, utils_1.writeFile)(`${this.projectPath}/App.js`, output);
                });
            });
        });
    }
    generatePageConfig(pageConfigs) {
        pageConfigs = pageConfigs.filter((p) => p.type == 'PAGE');
        const output = PAGE_CONFIG_TEMPLATE({
            pageConfigs: pageConfigs,
            pageConfigsStr: JSON.stringify(pageConfigs),
            lazyload: profile_1.default.lazyloadPages
        });
        (0, utils_1.writeFile)(`${this.projectPath}/src/pages/pages-config.js`, output);
    }
    generatePartialConfig(pageConfigs) {
        pageConfigs = pageConfigs.filter((p) => this.isPartial(p.type));
        const output = PAGE_CONFIG_TEMPLATE({
            pageConfigs: pageConfigs,
            pageConfigsStr: JSON.stringify(pageConfigs),
            lazyload: profile_1.default.lazyloadPartials
        });
        (0, utils_1.writeFile)(`${this.projectPath}/src/partials/partial-config.js`, output);
    }
    transformStyle(styles) {
        if (!styles) {
            return '{}';
        }
        const splitStr = '/*REACT_NATIVE_STYLES*/';
        const splitAt = styles.indexOf(splitStr);
        if (splitAt > 0) {
            styles = styles.substr(splitAt + splitStr.length);
        }
        return this.themeService.generateReactNativeStyles(styles);
    }
    generateDesignStyles(styles) {
        if (!styles) {
            return '';
        }
        return this.themeService.generateStudioStyles(styles);
    }
    async generateComponent(info, type, name, dynamicProps) {
        if (this.incBuilder && !this.incBuilder.isFragmentModified(name)) {
            return Promise.resolve();
        }
        if (this.incBuilder) {
            console.log(`Generating ${name} component.`);
        }
        const lType = type.toLowerCase().replace('-', '');
        const typeLabel = type.split('-').map(t => (0, lodash_1.capitalize)(t)).join('');
        const variables = JSON.parse(info.variables);
        Object.keys(variables).map((key) => {
            if (variables[key]["category"] === 'wm.LiveVariable') {
                this.liveVariables.push(variables[key]);
            }
        });
        this.setDeviceVariables(variables);
        const dest = type === 'PREFAB' ? `${this.projectPath}/src/pages/${name}` : `${this.projectPath}/src/${lType}s/${name}`;
        try {
            const output = this.prepareVariables(variables, typeLabel, []);
            (0, utils_1.writeFile)(`${dest}/${name}.variables.js`, output);
            const transpileArgs = {
                markup: info.markup,
                isPartOfPrefab: this.isPrefabApp,
                rnConfig: this.rnConfig,
            };
            const transpiledOutput = (0, transpile_1.transpileMarkup)(transpileArgs);
            if (info.markup.includes('metadata')) {
                if (this.isPrefabApp) {
                    transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../../../../component/dynamic/form.generator` });
                }
                else {
                    transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../component/dynamic/form.generator` });
                }
            }
            if (this.isPrefabApp) {
                transpiledOutput.imports.forEach(i => {
                    if (i.from.indexOf('/prefabs/') > 0) {
                        i.from = '../../../' + i.from;
                    }
                });
            }
            const imports = (0, lodash_1.sortBy)((0, lodash_1.uniqBy)(transpiledOutput.imports, i => i.name + i.from), i => i.name);
            const component = COMPONENT_TEMPLATE({
                name: name,
                type: type,
                lType: lType,
                typeLabel: typeLabel,
                dynamicPrefabProps: this.dynamicPrefabProps || {},
                dynamicProps: dynamicProps || {},
                prefabName: this.prefabName,
                markup: '\n' + transpiledOutput.markup,
                components: transpiledOutput.components,
                startUpVariables: Object.values(variables).filter((v) => v.startUpdate && v.category !== 'wm.TimerVariable').map((v) => v.name),
                startUpActions: Object.values(variables).filter((v) => v.startUpdate && v.category === 'wm.TimerVariable').map((v) => v.name),
                autoUpdateVariables: Object.values(variables).filter((v) => v.autoUpdate || ((v.category === 'wm.Variable' || v.category === 'wm.LiveVariable') && v.dataBinding && v.dataBinding.length)).map((v) => v.name),
                eagerImports: imports.filter(i => !i.lazy),
                lazyImports: imports.filter(i => i.lazy)
            });
            (0, utils_1.writeFile)(`${dest}/${name}.component.js`, component);
            const script = SCRIPT_TEMPLATE({
                typeLabel: type === 'CUSTOM-WIDGET' ? 'Widget' : typeLabel,
                script: info.script
            });
            (0, utils_1.writeFile)(`${dest}/${name}.script.js`, script);
            const styles = STYLE_TEMPLATE({
                styles: this.transformStyle(info.styles)
            });
            (0, utils_1.writeFile)(`${dest}/${name}.style.js`, styles);
            if (profile_1.default.targetPlatform === 'web') {
                this.savePageDesignStyles(this.generateDesignStyles(info.styles), name);
            }
            return Promise.all(transpiledOutput.prefabs.map(p => this.generatePrefabComponent(p, false))).then(() => {
                return Promise.all([
                    ...transpiledOutput.customWidgets.map(p => this.generateCustomWidget(p, false)),
                    ...transpiledOutput.xcomponents.map(c => this.generateWMXComponents(c, false))
                ]);
            });
        }
        catch (e) {
            console.log(`failed to generate component ${dest}/${name}`);
            throw e;
        }
    }
    getPropertiesInfo(info) {
        var _a;
        const dynamicProps = {};
        const propConfig = {};
        Object.keys(((_a = info.config) === null || _a === void 0 ? void 0 : _a.properties) || {}).map((k) => {
            const value = info.config.properties[k].value;
            if ((0, lodash_1.isString)(value) && value.startsWith('bind:')) {
                let exStr = decodeURIComponent(value.substring(5));
                dynamicProps[k] = `this.eval(() => ${(0, bind_ex_transformer_1.default)(exStr, '_this', 'attr')})`;
                propConfig[k] = null;
            }
            else {
                propConfig[k] = value === undefined ? null : value;
            }
        });
        let propsStr = JSON.stringify(propConfig);
        if (info.config.events) {
            propsStr = propsStr.replace(/\}$/, '');
            Object.keys(info.config.events).map((k) => {
                propsStr += `${propsStr.length === 1 ? '' : ','} ${k} : () => {}`;
            });
            propsStr += '}';
        }
        return {
            props: info.config.properties,
            dynamicProps,
            propsCode: COMPONENT_PROPS_TEMPLATE({
                props: propsStr
            })
        };
    }
    generatePrefabProps(name, projectPath) {
        return this.projectService.getPrefabInfo(name)
            .then(info => {
            const { dynamicProps, propsCode } = this.getPropertiesInfo(info);
            (0, utils_1.writeFile)(`${projectPath}/src/prefabs/${name}/src/pages/Main/Main.props.js`, propsCode);
            return dynamicProps;
        });
    }
    generatePrefabComponent(name, overwrite = true) {
        var _a;
        if ((!overwrite && this.transpiledComponents.prefabs[name])
            || (this.incBuilder && !((_a = this.incBuilder) === null || _a === void 0 ? void 0 : _a.isPrefabModified(name)))) {
            return Promise.resolve();
        }
        if (this.incBuilder) {
            console.log(`Generating ${name} prefab component.`);
        }
        const projectPath = this.isPrefabApp ? this.projectPath.split('/src/prefabs/')[0] : this.projectPath;
        const appUrl = this.isPrefabApp ? this.appUrl.split('/app/prefabs/')[0] : this.appUrl;
        this.transpiledComponents.prefabs[name] = true;
        return this.generatePrefabProps(name, projectPath).then(dynamicPrefabProps => {
            const prefabGenerator = new AppGenerator(`${projectPath}/src/prefabs/${name}`, this.projectService.getPrefabProjectService(name), `${appUrl}/app/prefabs/${name}`, name, undefined, dynamicPrefabProps);
            return prefabGenerator.generatePagesAndPartials()
                .then(() => prefabGenerator.generateResourceResolver());
        });
    }
    generateAllPrefabComponents(overwrite) {
        return this.projectService.getPrefabs().then(prefabs => {
            return Promise.all(prefabs === null || prefabs === void 0 ? void 0 : prefabs.map(prefab => this.generatePrefabComponent(prefab, overwrite)));
        });
    }
    compileXComponentLib() {
        return (0, execa_1.default)('npm', ['install'], {
            cwd: `${this.projectService.path}/extensions/components`
        }).then(() => {
            return (0, execa_1.default)('npm', ['run', 'build'], {
                cwd: `${this.projectService.path}/extensions/components`
            });
        }).then(() => {
            fs_extra_1.default.copySync(`${this.projectService.path}/extensions/components/build`, `${this.projectPath}/wmx-components`);
        });
    }
    async generateWMXComponents(name, overwrite) {
        try {
            if (!overwrite && this.transpiledComponents.wmxComponents[name]) {
                return Promise.resolve();
            }
            if (this.incBuilder) {
                console.log(`Generating ${name} component.`);
            }
            const wmxConfig = await this.projectService.getWMXConfig(name);
            this.transpiledComponents.wmxComponents[name] = true;
            let componentScript = '';
            if (profile_1.default.targetPlatform === 'web' && !wmxConfig.webSupport) {
                componentScript = WMX_COMPONENT_WEB_TEMPLATE({});
            }
            else {
                const styles = Object.keys(wmxConfig.styles || {}).map((k) => {
                    const style = wmxConfig.styles[k];
                    const name = style['name'] || k;
                    return {
                        name: name,
                        style: (0, lodash_1.isEmpty)(style['style']) ? 'undefined' : JSON.stringify(style['style'])
                    };
                });
                componentScript = WMX_COMPONENT_NATIVE_TEMPLATE({
                    name: name,
                    componentName: (0, lodash_1.capitalize)(name),
                    cssClassName: `app-x-${name.toLowerCase()}`,
                    module: wmxConfig.module,
                    props: Object.keys(wmxConfig.props || {}).map((k) => {
                        const prop = wmxConfig.props[k];
                        const name = prop.name || k;
                        return {
                            propertyName: name,
                            name: name.toLowerCase().replace(/[^\w]/g, '').replace(/_/g, ''),
                            defaultValue: (prop.defaultValue ? JSON.stringify(prop.defaultValue) : prop.defaultValue) || 'undefined'
                        };
                    }),
                    events: Object.keys(wmxConfig.events || {}).map((k) => {
                        const event = wmxConfig.events[k];
                        let name = event.name || k;
                        const eventName = name;
                        name = name.replace(/[^\w]/g, '').replace(/_/g, '');
                        if (name.toLowerCase().startsWith('on')) {
                            name = name.toLowerCase();
                            name = 'on' + name.charAt(2).toUpperCase() + name.substring(3);
                        }
                        return {
                            eventName: eventName,
                            name: name
                        };
                    }),
                    styles: styles
                });
            }
            (0, utils_1.writeFile)(`${this.projectPath}/wmx-components/src/${name}/index.wmx.js`, componentScript);
        }
        catch (e) {
            console.error(`Failed to generate ${name} WMX Component.`);
            throw e;
        }
    }
    generatePageComponent(name, overwrite = true) {
        if (!overwrite && this.transpiledComponents.pages[name]) {
            return Promise.resolve();
        }
        this.transpiledComponents.pages[name] = true;
        return this.projectService.getPageInfo(name).then(info => {
            return this.generateComponent(info, this.isPrefabApp ? 'PREFAB' : 'PAGE', name);
        });
    }
    generatePartialComponent(name, overwrite = true) {
        if (!overwrite && this.transpiledComponents.partials[name]) {
            return Promise.resolve();
        }
        this.transpiledComponents.partials[name] = true;
        return this.projectService.getPageInfo(name).then(info => {
            return this.generateComponent(info, 'PARTIAL', name);
        });
    }
    generateCustomWidget(name, overwrite = true) {
        if (!overwrite && this.transpiledComponents.partials[name]) {
            return Promise.resolve();
        }
        this.transpiledComponents.customWidgets[name] = true;
        return this.projectService.getCustomWidgetInfo(name).then(async (info) => {
            const { dynamicProps, propsCode } = this.getPropertiesInfo(info);
            (0, utils_1.writeFile)(`${this.projectPath}/src/custom-widgets/${name}/${name}.props.js`, propsCode);
            return this.generateComponent(info, 'CUSTOM-WIDGET', name, dynamicProps);
        });
    }
    generateServiceDefs() {
        return this.projectService.getServiceDefs().then(serviceDefs => {
            const output = SERVICE_DEFINITIONS({ serviceDefs: JSON.stringify(serviceDefs) });
            (0, utils_1.writeFile)(`${this.projectPath}/src/service-definitions.js`, output);
        });
    }
    generateBootstrapScript() {
        if (this.incBuilder) {
            console.log(`Generating bootstrap.js .`);
        }
        const config = fs_extra_1.default.readJsonSync(`${this.projectPath}/wm_rn_config.json`, {
            encoding: 'utf8'
        });
        let appUrl = this.appUrl;
        if (!(0, lodash_1.isEmpty)(config.serverPath) && config.serverPath !== '{{DEVELOPMENT_URL}}') {
            appUrl = config.serverPath;
        }
        if (appUrl.endsWith('/')) {
            appUrl = appUrl.substring(0, appUrl.length - 1);
        }
        const prefabUsed = [];
        Object.keys(this.transpiledComponents.prefabs).forEach((key) => {
            if (this.transpiledComponents.prefabs[key]) {
                prefabUsed.push(key);
            }
        });
        const output = BOOTSTRAP_TEMPLATE({
            appUrl: appUrl,
            enableLogs: config.preferences.enableLogs,
            loader: config.loader || 'skeleton',
            prefabUsed: prefabUsed
        });
        (0, utils_1.writeFile)(`${this.projectPath}/bootstrap.js`, output);
    }
    generateBabelConfig() {
        if (this.incBuilder) {
            console.log(`Generating babel config.`);
        }
        const config = fs_extra_1.default.readJsonSync(`${this.projectPath}/wm_rn_config.json`, {
            encoding: 'utf8'
        });
        const output = BABEL_CONFIG_TEMPLATE({
            enableLogs: config.preferences.enableLogs,
            isWeb: profile_1.default.targetPlatform === 'web'
        });
        (0, utils_1.writeFile)(`${this.projectPath}/babel.config.js`, output);
    }
    generateAppStyles() {
        if (this.incBuilder) {
            console.log(`Generating app.styles.js .`);
        }
        return this.projectService.getAppCss().then(response => {
            const styles = STYLE_TEMPLATE({
                styles: this.transformStyle(response)
            });
            (0, utils_1.writeFile)(`${this.projectPath}/app.style.js`, styles);
            if (profile_1.default.targetPlatform === 'web') {
                return this.saveAppDesignStyles(this.generateDesignStyles(response));
            }
        });
    }
    generateThemeVariables() {
        if (this.incBuilder) {
            console.log(`Generating Theme variables.`);
        }
        return this.projectService.getAppThemeVariables().then(response => {
            (0, utils_1.writeFile)(`${this.projectPath}/app.theme.variables.js`, response);
        }, () => { });
    }
    generateFormatters() {
        if (this.incBuilder) {
            console.log(`Generating formatters.`);
        }
        return this.projectService.getFormatters()
            .then((script) => {
            const code = FORMATTERS_TEMPLATE({
                script: script
            });
            (0, utils_1.writeFile)(`${this.projectPath}/src/extensions/formatters.js`, code);
        });
    }
    getAllResources(filter = (str) => true, dirPath = this.projectPath + '/assets/resources', pathPrefix = 'resources', arrayOfFiles = []) {
        if (fs_extra_1.default.existsSync(dirPath)) {
            fs_extra_1.default.readdirSync(dirPath).forEach((file) => {
                if (fs_extra_1.default.statSync(dirPath + "/" + file).isDirectory()) {
                    arrayOfFiles = this.getAllResources(filter, dirPath + "/" + file, pathPrefix + '/' + file, arrayOfFiles);
                }
                else if (filter(dirPath + "/" + file)) {
                    const ext = (0, lodash_1.last)(file.split('.'));
                    if (file.indexOf('@') < 0 && ALLOWED_RESOURCE_EXT.indexOf(ext || '') >= 0) {
                        arrayOfFiles.push(pathPrefix + "/" + file);
                    }
                }
            });
        }
        return arrayOfFiles;
    }
    /**
     * @description Get all widget imports in the generated app
     * @returns array of widgets imported (e.g, WmCamera, WmVideo)
     */
    async getWidgetImports() {
        let widgetImports = [];
        const pageConfigs = await this.projectService.getPageConfigs();
        await Promise.all(pageConfigs.map(async (p) => {
            const info = await this.projectService.getPageInfo(p.name);
            const transpileArgs = {
                markup: info.markup,
                isPartOfPrefab: this.isPrefabApp,
                rnConfig: this.rnConfig,
            };
            const transpiledOutput = (0, transpile_1.transpileMarkup)(transpileArgs);
            if (info.markup.includes('metadata')) {
                if (this.isPrefabApp) {
                    transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../../../../component/dynamic/form.generator` });
                }
                else {
                    transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../component/dynamic/form.generator` });
                }
            }
            if (this.isPrefabApp) {
                transpiledOutput.imports.forEach(i => {
                    if (i.from.indexOf('/prefabs/') > 0) {
                        i.from = '../../../' + i.from;
                    }
                });
            }
            const uniqueImports = (0, lodash_1.sortBy)((0, lodash_1.uniqBy)(transpiledOutput.imports, i => i.name + i.from), i => i.name);
            if (Array.isArray(uniqueImports) && uniqueImports.length > 0) {
                widgetImports.push(...uniqueImports);
            }
        }));
        const uniqueWidgetImports = (0, lodash_1.uniq)(widgetImports);
        return uniqueWidgetImports;
    }
    /**
     * Check for widget usage (e.g, WmCamera, WmVideo)
     * in component files (page, partial, prefab) of the generated app
     * and remove unused packages accordingly
     */
    async checkWidgetUsageInComponentFiles() {
        if (this.incBuilder) {
            console.log(`Checking widget usage in component files.`);
        }
        const projectDependencies = (0, lodash_1.uniq)([...this.projectPackageDependencies]);
        const imports = await this.getWidgetImports();
        projectDependencies.forEach((dep, index) => {
            const isWmTagImported = imports.findIndex((i) => {
                var _a;
                const hasTagName = (_a = dep.tagName) === null || _a === void 0 ? void 0 : _a.includes(i === null || i === void 0 ? void 0 : i.name);
                return hasTagName;
            }) >= 0;
            if (isWmTagImported && projectDependencies[index]) {
                projectDependencies[index].removePlugin = false;
            }
        });
        this.projectPackageDependencies = projectDependencies;
        // * Write widgetImports to build-cache if it's changed compared to previous build
        const widgetImports = projectDependencies.filter((dep) => !dep.removePlugin);
        const buildCachePath = `${this.projectPath}/.build-cache`;
        if (fs_extra_1.default.existsSync(buildCachePath)) {
            const buildCache = fs_extra_1.default.readJSONSync(buildCachePath);
            if (!buildCache.widgetImports) {
                buildCache.widgetImports = [];
            }
            const excludeVariables = ['Device'];
            const filteredWidgetImports = widgetImports.filter((v) => !excludeVariables.includes(v.name));
            if (!(0, lodash_1.isEqual)(buildCache.widgetImports, filteredWidgetImports)) {
                // console.log('<== Project widget imports changed ==>');
                this.shouldDoUnwantedPackagesCleanup = true;
                buildCache.widgetImports = filteredWidgetImports;
                fs_extra_1.default.writeJSONSync(buildCachePath, buildCache);
            }
            else if (this.incBuilder) {
                // * If widget imports are the same, return early
                return;
            }
        }
        else {
            fs_extra_1.default.writeJSONSync(buildCachePath, {});
        }
    }
    /**
     * @description Get the list of device variables used
     * in the app (App + Page owner)
     * @returns deviceVariables: any[]
     */
    async getDeviceVariables() {
        let deviceVariables = [];
        let allPagesDeviceVariables = [];
        const pageConfigs = await this.projectService.getPageConfigs();
        const appVariables = await this.projectService.getAppVariables();
        const appDeviceVariables = Object.values(appVariables).filter((v) => v.category === 'wm.DeviceVariable');
        await Promise.all(pageConfigs.map(async (p) => {
            const info = await this.projectService.getPageInfo(p.name);
            const pageVariables = JSON.parse(info.variables);
            const pageDeviceVariables = Object.values(pageVariables).filter((v) => v.category === 'wm.DeviceVariable');
            allPagesDeviceVariables.push(...pageDeviceVariables);
        }));
        const allDeviceVariables = [...appDeviceVariables, ...allPagesDeviceVariables];
        allDeviceVariables.forEach((v) => {
            const selectedOperation = operationMap[v.operation];
            if (!selectedOperation) {
                console.log('Cannot find operation named ' + v.operation);
            }
            if (deviceVariables.find((dv) => dv.operation === v.operation)) {
                return;
            }
            deviceVariables.push({
                'operation': v.operation,
                'service': v.service,
                'filename': selectedOperation.filename,
                'method': selectedOperation.method,
                'hasparams': selectedOperation.hasConstructorParams,
                'type': selectedOperation.name
            });
        });
        return deviceVariables;
    }
    /**
     * Scan all JS files in the project for imports that match packages in operationMap
     */
    scanJSFilesForImports() {
        this.jsFileImports.clear();
        const scanFileIgnoreList = ['device-operation-loader.js', 'device-permission-service.js', 'device-plugin-service.js', 'plugin-provider.js'];
        // Get all packages from operationMap
        const allOperationPackages = new Set();
        Object.values(operationMap).forEach((operation) => {
            if (operation.packages && Array.isArray(operation.packages)) {
                operation.packages.forEach((pkg) => allOperationPackages.add(pkg));
            }
        });
        const scanDirectory = (dirPath) => {
            if (!fs_extra_1.default.existsSync(dirPath)) {
                return;
            }
            fs_extra_1.default.readdirSync(dirPath).forEach((file) => {
                const fullPath = path_2.default.join(dirPath, file);
                const stat = fs_extra_1.default.statSync(fullPath);
                if (stat.isDirectory()) {
                    scanDirectory(fullPath);
                }
                else if (file.endsWith('.js') || file.endsWith('.ts') || file.endsWith('.tsx')) {
                    try {
                        // Ignore checking for package imports in ignore list file names
                        if (scanFileIgnoreList.includes(file)) {
                            return;
                        }
                        const content = fs_extra_1.default.readFileSync(fullPath, 'utf-8');
                        // Look for import statements that match operationMap packages
                        allOperationPackages.forEach(packageName => {
                            // Check for various import patterns:
                            // import ... from 'expo-location'
                            // import('expo-location')
                            // require('expo-location')
                            const importPatterns = [
                                new RegExp(`import\\s+.*?from\\s+['"\`]${packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"\`]`, 'g'),
                                new RegExp(`import\\s*\\(\\s*['"\`]${packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"\`]\\s*\\)`, 'g'),
                                new RegExp(`require\\s*\\(\\s*['"\`]${packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"\`]\\s*\\)`, 'g')
                            ];
                            if (importPatterns.some(pattern => pattern.test(content))) {
                                this.jsFileImports.add(packageName);
                            }
                        });
                    }
                    catch (error) {
                        console.warn(`Could not read file ${fullPath}:`, error);
                    }
                }
            });
        };
        // Scan all potential directories where JS files might be
        const scanPaths = [
            `${this.projectPath}/src`,
            `${this.projectPath}/assets/resources`,
        ];
        scanPaths.forEach(scanPath => {
            scanDirectory(scanPath);
        });
        if (this.jsFileImports.size > 0) {
            console.log(`Found imports for packages in JS files: ${Array.from(this.jsFileImports).join(', ')}`);
        }
    }
    checkPackageUsageInJSFiles() {
        if (this.incBuilder) {
            console.log(`Checking package usage in JS files.`);
        }
        // Scan JS files for imports before processing package.json
        this.scanJSFilesForImports();
    }
    generateFontConfig() {
        if (this.incBuilder) {
            console.log(`Generating Font config.`);
        }
        return this.projectService.getFontConfig().then(code => {
            (0, utils_1.writeFile)(`${this.projectPath}/font.config.js`, code);
        }).catch(() => { });
    }
    generateResourceResolver() {
        if (this.incBuilder) {
            console.log(`Copying Resources.`);
        }
        return this.projectService.getResources().then(src => {
            if (src) {
                fs_extra_1.default.copySync(src, this.projectPath + '/assets/resources', {
                    filter: (src) => {
                        return (!(profile_1.default.targetPlatform === 'web'
                            && src.endsWith('.native.js'))
                            && !(profile_1.default.targetPlatform === 'native'
                                && src.endsWith('.web.js'))
                            && (profile_1.default.copyResources
                                || src.endsWith('.js')
                                || src.indexOf('resources/icons/') >= 0
                                || fs_extra_1.default.lstatSync(src).isDirectory()));
                    }
                });
            }
        }).then(() => {
            const resources = profile_1.default.copyResources ? this.getAllResources() : this.getAllResources((src) => src.endsWith('.js'));
            const content = RESOURCE_RESOLVER_TEMPLATE({
                resources: resources,
                path: '../../assets/'
            });
            (0, utils_1.writeFile)(`${this.projectPath}/src/resolve/resource.resolver.js`, content);
        });
    }
    generateLocaleResolver() {
        if (this.incBuilder) {
            console.log(`Generating Locale resolver.`);
        }
        return Promise.resolve().then(() => {
            const resources = profile_1.default.useLocalMetadata ? this.getAllResources(() => true, this.projectPath + '/i18n', 'i18n') : [];
            const content = RESOURCE_RESOLVER_TEMPLATE({
                resources: resources,
                path: '../../'
            });
            (0, utils_1.writeFile)(`${this.projectPath}/src/resolve/locale.resolver.js`, content);
        });
    }
    copyi18nFiles() {
        if (profile_1.default.useLocalMetadata) {
            if (this.incBuilder) {
                console.log(`Copying i18n files.`);
            }
            return this.projectService.geti18NFiles().then(p => {
                if (p) {
                    fs_extra_1.default.copySync(p, this.projectPath + '/i18n');
                }
            });
        }
        return Promise.resolve();
    }
    generatePagesAndPartials() {
        let pageConfigs = null;
        return this.projectService.getPageConfigs().then(_pageConfigs => {
            pageConfigs = _pageConfigs;
            const pageCreationPromises = pageConfigs.map(p => {
                if (p.type === 'PAGE') {
                    return this.generatePageComponent(p.name);
                }
                if (this.isPartial(p.type)) {
                    return this.generatePartialComponent(p.name);
                }
            });
            return Promise.all(pageCreationPromises);
        })
            .then(() => this.generatePageConfig(pageConfigs))
            .then(() => this.generatePartialConfig(pageConfigs));
    }
    async mergePackageJson() {
        if (this.incBuilder) {
            console.log(`Merging package.json .`);
        }
        const config = await this.projectService.getRNConfig().then(data => JSON.parse(data));
        let packageToOveride = fs_extra_1.default.readJSONSync(`${this.projectPath}/package.json`, { encoding: 'utf-8' });
        packageToOveride.name = config.name;
        packageToOveride.version = config.version;
        packageToOveride.description = config.description;
        if (profile_1.default.targetPlatform === 'web') {
            const webPackageJson = fs_extra_1.default.readJSONSync(`${__dirname}/templates/package.web.json`, { encoding: 'utf-8' });
            packageToOveride = (0, lodash_1.merge)(packageToOveride, webPackageJson);
        }
        if (this.wmxComponents.length > 0) {
            packageToOveride.scripts.postinstall = [
                'npm install ./wmx-components',
                ...((packageToOveride.scripts.postinstall || '')
                    .split('&&')
                    .filter((s) => s && !!s.trim()))
            ].join(' && ');
            packageToOveride.dependencies['@wavemaker/wmx-component'] = 'file:wmx-components';
            if (!this.incBuilder || this.incBuilder.isWMXComponentModified()) {
                await this.compileXComponentLib();
            }
        }
        const packageOverride = await this.projectService.getPackageJSONOverride();
        if (packageOverride) {
            const packageOverriden = (0, lodash_1.merge)(packageToOveride, packageOverride);
            fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(packageOverriden, null, 4));
        }
    }
    /**
     * update app.json with expo-video plugin
     * @param updatedPackageJson
     */
    async updateAppJsonWithExpoVideoPlugin(updatedPackageJson) {
        var _a, _b, _c;
        const appJson = fs_extra_1.default.readJsonSync(`${this.projectPath}/app.json`, { encoding: 'utf-8' });
        const hasExpoVideoPackage = !!((_a = updatedPackageJson === null || updatedPackageJson === void 0 ? void 0 : updatedPackageJson.dependencies) === null || _a === void 0 ? void 0 : _a['expo-video']);
        const customAppJson = await this.projectService.getAppJSON();
        const customAppJsonExpoVideoPlugin = (_c = (_b = customAppJson === null || customAppJson === void 0 ? void 0 : customAppJson.expo) === null || _b === void 0 ? void 0 : _b.plugins) === null || _c === void 0 ? void 0 : _c.filter((plugin) => Array.isArray(plugin) && plugin[0] === 'expo-video' || typeof plugin === 'string' && plugin === 'expo-video');
        const hasExpoVideoPluginInCustomAppJson = (customAppJsonExpoVideoPlugin === null || customAppJsonExpoVideoPlugin === void 0 ? void 0 : customAppJsonExpoVideoPlugin.length) > 0;
        const expoVideoPluginEntry = ["expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true }];
        const pluginExists = appJson.expo.plugins.some((plugin) => {
            return Array.isArray(plugin) && plugin[0] === "expo-video" || typeof plugin === 'string' && plugin === 'expo-video';
        });
        if (pluginExists) {
            return;
        }
        if (hasExpoVideoPackage && !hasExpoVideoPluginInCustomAppJson) {
            // keep original expo-video plugin array
            appJson.expo.plugins.push(expoVideoPluginEntry);
            (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
        }
        else if (!hasExpoVideoPackage && hasExpoVideoPluginInCustomAppJson) {
            // keep expo-video plugin array from custom app.json
            appJson.expo.plugins.push(...customAppJsonExpoVideoPlugin);
            (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
        }
        else if (hasExpoVideoPackage && hasExpoVideoPluginInCustomAppJson) {
            // keep expo-video plugin array from custom app.json
            appJson.expo.plugins.push(...customAppJsonExpoVideoPlugin);
            (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
        }
    }
    /**
     * remove unused dependencies from package.json
     * @param packageJsonData
     * @returns updatedPackageJson, pluginsRemoved
     */
    async removeUnusedPluginsFromPackageJson(packageJsonData) {
        var _a;
        let updatedPackageJson = (0, lodash_1.cloneDeep)(packageJsonData);
        let pluginsRemoved = false;
        let removedPackages = [];
        try {
            const dependencies = (0, lodash_1.cloneDeep)(this.projectPackageDependencies);
            const listOfPackagesUsed = await this.getListOfPackagesUsedInGeneratedApp();
            const operationsUsed = Object.entries(listOfPackagesUsed)
                .map(([key, value]) => {
                if (value) {
                    return key.replace(/^has/, "").replace(/Plugin$/, "");
                }
            })
                .filter((value) => value !== undefined);
            // iterating over each operation of the operationMap
            for (let i = 0; i < dependencies.length; i++) {
                const removePlugin = dependencies[i].removePlugin;
                const deviceOperationName = dependencies[i].name;
                const deviceOperationMethod = dependencies[i].name === 'File' ? (_a = dependencies[i].method) === null || _a === void 0 ? void 0 : _a.replace('Operation', '') : dependencies[i].name;
                const operationFound = operationsUsed.includes(deviceOperationMethod);
                const currentServiceUsedByOtherOperation = dependencies
                    .filter((dep) => { var _a; return dep.name !== deviceOperationName && ((_a = dep.packages) === null || _a === void 0 ? void 0 : _a.some((p) => dependencies[i].packages.includes(p))); }) // other operations that are using at least one of the packages of the current operation
                    .filter((dep) => {
                    var _a;
                    const depName = dep.name === 'File' ? (_a = dep.method) === null || _a === void 0 ? void 0 : _a.replace('Operation', '') : dep.name;
                    return operationsUsed.includes(depName);
                });
                // Check if any packages from this operation are imported in JS files
                const hasImportsInJSFiles = dependencies[i].packages.some((pkg) => this.jsFileImports.has(pkg));
                /**
                 * Remove package if:
                 * 1. removePlugin is true
                 * 2. the current operation is not used in the app
                 * 3. the packages of the current operation are not imported in JS files
                 */
                const removePackage = removePlugin && !operationFound && !hasImportsInJSFiles;
                // console.log("<---Remove current package--->", "\ndependencies[i].method:", dependencies[i].method, "\nremovePackage:", removePackage, "\nremovePlugin:", removePlugin, "\noperationFound:", operationFound, "\nhasImportsInJSFiles:", hasImportsInJSFiles);
                if (removePackage) {
                    dependencies[i].packages.forEach((p) => {
                        // whether this specific package of the current operation is used by other operations
                        const isPackageUsedByOtherOperation = currentServiceUsedByOtherOperation.filter((operation) => operation.packages.includes(p)).length > 0;
                        if (isPackageUsedByOtherOperation) {
                            return;
                        }
                        delete updatedPackageJson.dependencies[p];
                        if (!removedPackages.includes(p)) {
                            removedPackages.push(p);
                        }
                    });
                    pluginsRemoved = true;
                }
                if (hasImportsInJSFiles) {
                    console.log(`Keeping packages ${dependencies[i].packages.join(', ')} due to imports found in JS files`);
                }
            }
        }
        catch (error) {
            console.log('Failed to remove unused plugins from package json: ', error);
        }
        await this.updateAppJsonWithExpoVideoPlugin(updatedPackageJson);
        console.log(`Removed the following unused packages: ${removedPackages.join(', ')}`);
        return { updatedPackageJson, pluginsRemoved };
    }
    async removeUnusedPackages() {
        if (this.incBuilder) {
            console.log(`Removing unused packages.`);
        }
        let packageJsonToUpdate = fs_extra_1.default.readJSONSync(`${this.projectPath}/package.json`, { encoding: 'utf-8' });
        const templatePackageJson = fs_extra_1.default.readJSONSync(`${__dirname}/templates/project/package.json`, { encoding: 'utf-8' });
        const templateDependencies = templatePackageJson.dependencies;
        // * template dependencies + packages from wm_rn_config.json's plugins
        const mergedDependencies = (0, lodash_1.merge)(packageJsonToUpdate.dependencies, templateDependencies);
        packageJsonToUpdate.dependencies = mergedDependencies;
        const { updatedPackageJson, pluginsRemoved } = await this.removeUnusedPluginsFromPackageJson(packageJsonToUpdate);
        if (pluginsRemoved) {
            this.shouldNpmInstall = true;
        }
        fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(updatedPackageJson, null, 4));
        // * installing packages whenever a package is added or removed
        if (this.shouldNpmInstall && this.incBuilder) {
            (0, command_1.npmInstall)(`${this.projectPath}`);
        }
    }
    async copyPackageLockJson() {
        if (fs_extra_1.default.existsSync(`${this.projectPath}/package-lock.json`)) {
            return;
        }
        if (this.incBuilder) {
            console.log(`Copying package-lock.json .`);
        }
        if (profile_1.default.packageLockFilePath && fs_extra_1.default.existsSync(profile_1.default.packageLockFilePath)) {
            fs_extra_1.default.copySync(profile_1.default.packageLockFilePath, `${this.projectPath}/package-lock.json`);
        }
        if (profile_1.default.yarnLockFilePath && fs_extra_1.default.existsSync(profile_1.default.yarnLockFilePath)) {
            fs_extra_1.default.copySync(profile_1.default.yarnLockFilePath, `${this.projectPath}/yarn.json`);
        }
    }
    async copyScreenCapturePluginIfNeeded() {
        var _a;
        const config = this.rnConfig;
        const appJsonPath = `${this.projectPath}/app.json`;
        if (!fs_extra_1.default.existsSync(appJsonPath)) {
            console.warn(`Warning: app.json not found at ${appJsonPath}`);
            return;
        }
        const appJson = fs_extra_1.default.readJsonSync(appJsonPath);
        const screenCapturePluginEntry = ["./plugins/expo-screen-capture-plugin", {}];
        if ((_a = config === null || config === void 0 ? void 0 : config.screenCaptureProtection) === null || _a === void 0 ? void 0 : _a.enabled) {
            const pluginSourcePath = __dirname + '/templates/project/plugins/expo-screen-capture-plugin';
            const pluginDestPath = `${this.projectPath}/plugins/expo-screen-capture-plugin`;
            fs_extra_1.default.mkdirsSync(`${this.projectPath}/plugins`);
            if (fs_extra_1.default.existsSync(pluginSourcePath)) {
                fs_extra_1.default.copySync(pluginSourcePath, pluginDestPath);
                console.log(`Copied screen capture plugin to ${pluginDestPath}`);
            }
            else {
                console.warn(`Warning: Screen capture plugin source not found at ${pluginSourcePath}`);
                return;
            }
            if (!appJson.expo.plugins) {
                appJson.expo.plugins = [];
            }
            const pluginExists = appJson.expo.plugins.some((plugin) => {
                return Array.isArray(plugin) && plugin[0] === "./plugins/expo-screen-capture-plugin";
            });
            if (!pluginExists) {
                appJson.expo.plugins.push(screenCapturePluginEntry);
                console.log(`Added screen capture plugin to app.json`);
            }
        }
        else {
            const pluginDestPath = `${this.projectPath}/plugins/expo-screen-capture-plugin`;
            if (fs_extra_1.default.existsSync(pluginDestPath)) {
                fs_extra_1.default.removeSync(pluginDestPath);
                const pluginsDir = `${this.projectPath}/plugins`;
                if (fs_extra_1.default.existsSync(pluginsDir)) {
                    const remainingFiles = fs_extra_1.default.readdirSync(pluginsDir);
                    if (remainingFiles.length === 0) {
                        fs_extra_1.default.removeSync(pluginsDir);
                    }
                }
            }
            if (appJson.expo.plugins) {
                const originalLength = appJson.expo.plugins.length;
                appJson.expo.plugins = appJson.expo.plugins.filter((plugin) => {
                    if (Array.isArray(plugin)) {
                        return plugin[0] !== "./plugins/expo-screen-capture-plugin";
                    }
                    return true;
                });
                if (appJson.expo.plugins.length < originalLength) {
                    console.log(`Removed screen capture plugin from app.json`);
                }
            }
        }
        fs_extra_1.default.writeJsonSync(appJsonPath, appJson, { spaces: 2 });
    }
    async addCustomPluginsToPackageJson() {
        if (this.incBuilder) {
            console.log(`Adding custom plugins.`);
        }
        await this.projectService.getExpoPlugins().then(src => {
            if (src) {
                fs_extra_1.default.mkdirsSync(`${this.projectPath}/expo-plugins`);
                fs_extra_1.default.copySync(src, this.projectPath + '/expo-plugins');
            }
        });
        const config = fs_extra_1.default.readJsonSync(`${this.projectPath}/wm_rn_config.json`, {
            encoding: 'utf8'
        });
        let plugins = config.plugins;
        let data = fs_extra_1.default.readFileSync(`${this.projectPath}/package.json`, 'utf-8');
        const jsonData = JSON.parse(data);
        let shouldNpmInstall = false;
        const prefabs = [
            ...Object.keys((this.transpiledComponents.prefabs || {}))
        ];
        if (prefabs.length > 0) {
            prefabs.map(async (prefab) => {
                await this.projectService.getPrefabInfo(prefab).then(info => {
                    var _a;
                    (_a = info.config["resources"]["scripts"]) === null || _a === void 0 ? void 0 : _a.map((script) => {
                        if (script.includes('npm://')) {
                            if (jsonData['dependencies'][script.match(/[\/]+(.*?)@\d/)[1]] && semver_1.default.lt(jsonData['dependencies'][script.match(/[\/]+(.*?)@\d/)[1]], script.match('.*@(.*?)$')[1])) {
                                console.log("adding new version " + script.match('.*@(.*?)$')[1] + "for script.match(/[\/]+(.*?)@\d/)![1] dependency");
                                jsonData['dependencies'][script.match(/[\/]+(.*?)@\d/)[1]] = script.match('.*@(.*?)$')[1];
                            }
                            else {
                                jsonData['dependencies'][script.match(/[\/]+(.*?)@\d/)[1]] = script.match('.*@(.*?)$')[1];
                            }
                        }
                    });
                });
                plugins.forEach((p) => {
                    if (p.spec) {
                        if (p.spec.includes('tgz')) {
                            jsonData['dependencies'][p.name] = 'file:' + p.spec;
                            shouldNpmInstall = true;
                        }
                        else if (semver_1.default.valid(p.spec) && !semver_1.default.lt(p.spec, jsonData['dependencies'][p.name] || '0.0.0')) {
                            jsonData['dependencies'][p.name] = p.spec;
                            shouldNpmInstall = true;
                        }
                        else if (!semver_1.default.valid(p.spec)) {
                            jsonData['dependencies'][p.name] = p.spec;
                            shouldNpmInstall = true;
                        }
                    }
                });
                if (shouldNpmInstall) {
                    this.shouldNpmInstall = true;
                }
                fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(jsonData, null, 4));
            });
        }
        else {
            plugins.forEach((p) => {
                if (p.spec) {
                    if (p.spec.includes('tgz')) {
                        jsonData['dependencies'][p.name] = 'file:' + p.spec;
                        shouldNpmInstall = true;
                    }
                    else if (semver_1.default.valid(p.spec) && !semver_1.default.lt(p.spec, jsonData['dependencies'][p.name] || '0.0.0')) {
                        jsonData['dependencies'][p.name] = p.spec;
                        shouldNpmInstall = true;
                    }
                    else if (!semver_1.default.valid(p.spec)) {
                        jsonData['dependencies'][p.name] = p.spec;
                        shouldNpmInstall = true;
                    }
                }
            });
            if (shouldNpmInstall) {
                this.shouldNpmInstall = true;
            }
            fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(jsonData, null, 4));
        }
    }
    async generateFoundationTheme() {
        await new app_override_css_generator_1.AppOverrideCSSGenerator(this.projectService.path).generate();
        const tokenOverridesCSS = this.projectService.getAppTokenOverrides();
        const tokenOverrides = this.themeService.generateOverrideRNTokens(tokenOverridesCSS);
        const themePath = `${this.projectService.path}/design-tokens/foundation`;
        fs_extra_1.default.copySync(`${__dirname}/templates/foundation-theme/dist`, themePath);
        const colorSchemes = JSON.stringify(tokenOverrides.colorSchemes, null, 4);
        const variants = JSON.stringify(tokenOverrides.variants, null, 4);
        const content = `export default ${colorSchemes};\n\nexport const variants = ${variants};`;
        fs_extra_1.default.writeFileSync(`${this.projectPath}/app.designtokens.js`, content);
        return [themePath];
    }
    async generateAppTheme() {
        if (this.isPrefabApp) {
            return Promise.resolve();
        }
        if (this.incBuilder) {
            console.log(`Generating App theme.`);
        }
        const enableDesignTokens = await this.projectService.isDesignTokenEnabled();
        console.log('Design Tokens Enabled?', enableDesignTokens);
        return Promise.resolve()
            .then(() => {
            if (enableDesignTokens) {
                return this.generateFoundationTheme();
            }
            else {
                return this.projectService.getThemes();
            }
        }).then(themes => {
            return themes.map(t => {
                const themeName = (0, lodash_1.last)(t.split('/'));
                fs_extra_1.default.copySync(t, `${this.projectPath}/${enableDesignTokens ? 'design-tokens' : 'theme'}/${themeName}`);
                return themeName;
            });
        }).then(themes => {
            themes.map(t => [
                `${this.projectPath}/${enableDesignTokens ? 'design-tokens' : 'theme'}/${t}/android/assets`,
                `${this.projectPath}/${enableDesignTokens ? 'design-tokens' : 'theme'}/${t}/ios/assets`
            ]).flat().map(p => {
                const resources = this.getAllResources((str) => true, p, 'theme/assets');
                const content = THEME_ASSET_RESOLVER_TEMPLATE({
                    resources: resources,
                    path: '____'
                });
                (0, utils_1.writeFile)(`${p}/../asset.resolver.js`, content.replace(/\_\_\_\_theme\//g, './'));
            });
            return themes;
        }).then(themes => {
            const output = APP_THEME_TEMPLATE({
                themes: themes,
                enableDesignTokens: enableDesignTokens,
                selectedTheme: this.projectService.getSelectedTheme()
            });
            (0, utils_1.writeFile)(`${this.projectPath}/app.theme.js`, output);
        });
    }
    generateEntityMetadata() {
        if (this.incBuilder) {
            console.log(`Generating Entity data.`);
        }
        const liveSources = {};
        this.liveVariables.forEach((lv) => {
            const liveSource = lv.liveSource;
            const entityName = lv.type;
            if (!liveSources[liveSource]) {
                liveSources[liveSource] = {};
            }
            if (!liveSources[liveSource][entityName]) {
                liveSources[liveSource][entityName] = {
                    "propertiesMap": lv.propertiesMap,
                    "relatedTables": lv.relatedTables
                };
            }
        });
        Object.keys(liveSources).forEach((liveSource) => {
            (0, utils_1.writeFile)(`${this.projectPath}/metadata/entities/${liveSource}.json`, JSON.stringify(liveSources[liveSource], null, 4));
        });
        const output = ENTITY_PROVIDER_TEMPLATE({
            liveSources: Object.keys(liveSources)
        });
        (0, utils_1.writeFile)(`${this.projectPath}/metadata/entities/entity-provider.js`, output);
    }
    generateFonts() {
        return this.projectService.getFontConfig().then((code) => {
            var _a;
            const fontConfigUrl = `${this.projectPath}/font.config.js`;
            code = code.replace('export default', 'module.exports = ');
            (0, utils_1.writeFile)(fontConfigUrl, code);
            return (_a = fontConfigUrl, Promise.resolve().then(() => __importStar(require(_a)))).then((fontConfig) => {
                return Promise.all(fontConfig.default.fonts.map((font) => {
                    if (font.csspath) {
                        return this.themeService.getIconFontScripts(path_2.default.join(this.projectPath, '/assets/', font.csspath));
                    }
                    return;
                }));
            });
        }).then(() => this.generateResourceResolver());
    }
    async generateApp() {
        this.rnConfig = await this.projectService.getRNConfig().then(data => JSON.parse(data));
        this.incBuilder || fs_extra_1.default.copySync(__dirname + '/templates/project', this.projectPath);
        return this.generatePagesAndPartials()
            .then(() => this.incBuilder && this.generateAllPrefabComponents())
            .then(() => !this.incBuilder || this.incBuilder.isResourceModified())
            .then((build) => (build && this.generateResourceResolver()))
            .then(() => !this.incBuilder || this.incBuilder.isConfigModified())
            .then((build) => (build && this.generateAppConfig()))
            .then(() => !this.incBuilder || this.incBuilder.isAppScriptModified())
            .then((build) => (build && this.generateAppScript()))
            .then(() => !this.incBuilder || this.incBuilder.isAppStyleModified())
            .then((build) => (build && this.generateAppStyles()))
            .then(() => !this.incBuilder || this.incBuilder.isThemeVariablesModified())
            .then((build) => (build && this.generateThemeVariables()))
            .then(() => !this.incBuilder || this.incBuilder.isAppVariablesModified())
            .then((build) => (build && this.generateAppVariables()))
            .then(() => !this.incBuilder || this.incBuilder.isEntityModified())
            .then((build) => (build && this.generateEntityMetadata()))
            .then(() => !this.incBuilder || this.incBuilder.isFontConfigModified())
            .then((build) => (build && this.generateFontConfig()))
            .then(() => !this.incBuilder || this.incBuilder.isFontConfigModified())
            .then((build) => (build && this.generateFonts()))
            .then(() => !this.incBuilder || this.incBuilder.isConfigModified())
            .then((build) => (build && this.generateRNConfig()))
            .then(() => !this.incBuilder || this.incBuilder.isFormatterModified())
            .then((build) => (build && this.generateFormatters()))
            .then(() => !this.incBuilder || this.incBuilder.isConfigModified())
            .then((build) => (build && this.generateBootstrapScript()))
            .then(() => !this.incBuilder || this.incBuilder.isConfigModified())
            .then((build) => (build && this.generateBabelConfig()))
            .then(() => !this.incBuilder || this.incBuilder.isThemeModified() || this.incBuilder.isFoundationThemeModified())
            .then((build) => (build && this.generateAppTheme()))
            .then(() => !this.incBuilder || this.incBuilder.isi18nModified())
            .then((build) => (build && this.copyi18nFiles()))
            .then(() => !this.incBuilder || this.incBuilder.isi18nModified())
            .then((build) => (build && this.generateLocaleResolver()))
            .then(() => {
            if (this.incBuilder) {
                const components = this.incBuilder.getWMXModifiedComponents();
                return Promise.all(components.map(c => this.generateWMXComponents(c, true)));
            }
        }).then(() => !this.incBuilder)
            .then((build) => (this.prepareDeviceOperationLoader()))
            .then(() => !this.incBuilder
            || this.incBuilder.isConfigModified()
            || this.incBuilder.isPrefabModified())
            .then((build) => (build && this.addCustomPluginsToPackageJson()))
            .then(() => this.mergePackageJson())
            .then(() => this.copyPackageLockJson())
            .then(() => this.checkWidgetUsageInComponentFiles())
            .then(() => this.checkPackageUsageInJSFiles())
            .then(() => !this.incBuilder || this.shouldDoUnwantedPackagesCleanup)
            .then((removePackages) => {
            if (removePackages) {
                return this.removeUnusedPackages();
            }
            else {
                return Promise.resolve();
            }
        })
            .then(() => !this.incBuilder)
            .then((build) => (build && this.prettify()))
            .then(() => this.projectService.copyNpmPackages(this.projectPath))
            .then(() => this.copyScreenCapturePluginIfNeeded())
            .then(() => console.log('code generated at ' + this.projectPath));
    }
    generateFragment(name) {
        let pageConfigs = null;
        return this.projectService.getPageConfigs().then(_pageConfigs => {
            pageConfigs = _pageConfigs;
            const info = pageConfigs.find((config) => config.name === name);
            if (info) {
                if (info.type === 'PAGE') {
                    return this.generatePageComponent(info.name);
                }
                if (this.isPartial(info.type)) {
                    return this.generatePartialComponent(name);
                }
            }
        })
            .then(() => this.generatePageConfig(pageConfigs))
            .then(() => this.generatePartialConfig(pageConfigs))
            .then(() => this.prettify(`**/${name}/**/*.js`))
            .then(() => console.log('code generated at ' + this.projectPath));
    }
    ;
}
exports.default = AppGenerator;
//# sourceMappingURL=app.generator.js.map