<commit_before>﻿// Definitions.
import { LoadOptions } from ".";
import { View, ViewBase, Template, KeyedTemplate } from "../core/view";
import { ViewEntry } from "../frame";

// Types.
import { debug, ScopeError, SourceError, Source } from "../../utils/debug";
import * as xml from "../../xml";
import { File, path, knownFolders } from "../../file-system";
import { isString, isDefined } from "../../utils/types";
import { ComponentModule, setPropertyValue, getComponentModule } from "./component-builder";
import { platformNames, device } from "../../platform";
import { resolveFileName } from "../../file-system/file-name-resolver";
import { profile } from "../../profiling";
import * as traceModule from "../../trace";

const ios = platformNames.ios.toLowerCase();
const android = platformNames.android.toLowerCase();

const defaultNameSpaceMatcher = /tns\.xsd$/i;

var trace: typeof traceModule;
function ensureTrace() {
    if (!trace) {
        trace = require("trace");
    }
}

export function parse(value: string | Template, context: any): View {
    if (typeof value === "function") {
        return (<Template>value)();
    } else {
        const exports = context ? getExports(context) : undefined;
        const componentModule = parseInternal(value, exports);
        return componentModule && componentModule.component;
    }
}

export function parseMultipleTemplates(value: string, context: any): Array<KeyedTemplate> {
    const dummyComponent = `<ListView><ListView.itemTemplates>${value}</ListView.itemTemplates></ListView>`;
    return parseInternal(dummyComponent, context).component["itemTemplates"];
}

export function load(pathOrOptions: string | LoadOptions, context?: any): View {
    let componentModule: ComponentModule;
    if (!context) {
        if (typeof pathOrOptions === "string") {
            componentModule = loadInternal(pathOrOptions);
        } else {
            componentModule = loadCustomComponent(pathOrOptions.path, pathOrOptions.name, pathOrOptions.attributes, pathOrOptions.exports, pathOrOptions.page, true);
        }
    } else {
        let path = <string>pathOrOptions;
        componentModule = loadInternal(path, context);
    }

    return componentModule && componentModule.component;
}

export function loadPage(moduleNamePath: string, fileName: string, context?: any): View {
    const componentModule = loadInternal(fileName, context, moduleNamePath);
    return componentModule && componentModule.component;
}

const loadModule = profile("loadModule", (moduleNamePath: string, entry: ViewEntry): ModuleExports => {
    // web-pack case where developers register their page JS file manually.
    if (global.moduleExists(entry.moduleName)) {
        return global.loadModule(entry.moduleName);
    } else {
        let moduleExportsResolvedPath = resolveFileName(moduleNamePath, "js");
        if (moduleExportsResolvedPath) {
            // Exclude extension when doing require.
            moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3)
            return global.loadModule(moduleExportsResolvedPath);
        }
    }

    return null;
})

const viewFromBuilder = profile("viewFromBuilder", (moduleNamePath: string, moduleExports: any): View => {
    // Possible XML file path.
    const fileName = resolveFileName(moduleNamePath, "xml");
    if (fileName) {
        // Or check if the file exists in the app modules and load the page from XML.
        return loadPage(moduleNamePath, fileName, moduleExports);
    }

    // Attempts to implement https://github.com/NativeScript/NativeScript/issues/1311
    // if (page && fileName === `${moduleNamePath}.port.xml` || fileName === `${moduleNamePath}.land.xml`){
    //     page["isBiOrientational"] = true;
    // }

    return null;
})

export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEntry): View => {
    if (entry.create) {
        return createView(entry);
    } else if (entry.moduleName) {
        // Current app full path.
        const currentAppPath = knownFolders.currentApp().path;
        
        // Full path of the module = current app full path + module name.
        const moduleNamePath = path.join(currentAppPath, entry.moduleName);
        const moduleExports = loadModule(moduleNamePath, entry);

        if (moduleExports && moduleExports.createPage) {
            return moduleCreateView(moduleNamePath, moduleExports);
        } else {
            // cssFileName is loaded inside pageFromBuilder->loadPage
            return viewFromBuilder(moduleNamePath, moduleExports);
        }
    }
    
    throw new Error("Failed to load page XML file for module: " + entry.moduleName);
});

const createView = profile("entry.create", (entry: ViewEntry): View => {
    const view = entry.create();
    if (!view) {
        throw new Error("Failed to create Page with entry.create() function.");
    }

    return view;
});

interface ModuleExports {
    createPage?: () => View;
}

const moduleCreateView = profile("module.createView", (moduleNamePath: string, moduleExports: ModuleExports): View => {
    const view = moduleExports.createPage();
    const cssFileName = resolveFileName(moduleNamePath, "css");
    
    // If there is no cssFile only appCss will be applied at loaded.
    if (cssFileName) {
        view.addCssFile(cssFileName);
    }
    return view;
});

function loadInternal(fileName: string, context?: any, moduleNamePath?: string): ComponentModule {
    let componentModule: ComponentModule;

    // Check if the XML file exists.
    if (File.exists(fileName)) {
        const file = File.fromPath(fileName);
        const text = file.readTextSync((error) => { throw new Error("Error loading file " + fileName + " :" + error.message) });
        componentModule = parseInternal(text, context, fileName, moduleNamePath);
    }

    if (componentModule && componentModule.component) {
        // Save exports to root component (will be used for templates).
        (<any>componentModule.component).exports = context;
    }
<commit_msg>feat(builder): enable reading xml from bundle (#5668)<commit_after>﻿// Definitions.
import { LoadOptions } from ".";
import { View, ViewBase, Template, KeyedTemplate } from "../core/view";
import { ViewEntry } from "../frame";

// Types.
import { debug, ScopeError, SourceError, Source } from "../../utils/debug";
import * as xml from "../../xml";
import { File, path, knownFolders } from "../../file-system";
import { isString, isDefined } from "../../utils/types";
import { ComponentModule, setPropertyValue, getComponentModule } from "./component-builder";
import { platformNames, device } from "../../platform";
import { resolveFileName } from "../../file-system/file-name-resolver";
import { profile } from "../../profiling";
import * as traceModule from "../../trace";

const ios = platformNames.ios.toLowerCase();
const android = platformNames.android.toLowerCase();

const defaultNameSpaceMatcher = /tns\.xsd$/i;

var trace: typeof traceModule;
function ensureTrace() {
    if (!trace) {
        trace = require("trace");
    }
}

export function parse(value: string | Template, context: any): View {
    if (typeof value === "function") {
        return (<Template>value)();
    } else {
        const exports = context ? getExports(context) : undefined;
        const componentModule = parseInternal(value, exports);
        return componentModule && componentModule.component;
    }
}

export function parseMultipleTemplates(value: string, context: any): Array<KeyedTemplate> {
    const dummyComponent = `<ListView><ListView.itemTemplates>${value}</ListView.itemTemplates></ListView>`;
    return parseInternal(dummyComponent, context).component["itemTemplates"];
}

export function load(pathOrOptions: string | LoadOptions, context?: any): View {
    let componentModule: ComponentModule;
    if (!context) {
        if (typeof pathOrOptions === "string") {
            componentModule = loadInternal(pathOrOptions);
        } else {
            componentModule = loadCustomComponent(pathOrOptions.path, pathOrOptions.name, pathOrOptions.attributes, pathOrOptions.exports, pathOrOptions.page, true);
        }
    } else {
        let path = <string>pathOrOptions;
        componentModule = loadInternal(path, context);
    }

    return componentModule && componentModule.component;
}

export function loadPage(moduleNamePath: string, fileName: string, context?: any): View {
    const componentModule = loadInternal(fileName, context, moduleNamePath);
    return componentModule && componentModule.component;
}

const loadModule = profile("loadModule", (moduleNamePath: string, entry: ViewEntry): ModuleExports => {
    // web-pack case where developers register their page JS file manually.
    if (global.moduleExists(entry.moduleName)) {
        return global.loadModule(entry.moduleName);
    } else {
        let moduleExportsResolvedPath = resolveFileName(moduleNamePath, "js");
        if (moduleExportsResolvedPath) {
            // Exclude extension when doing require.
            moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3)
            return global.loadModule(moduleExportsResolvedPath);
        }
    }

    return null;
})

const viewFromBuilder = profile("viewFromBuilder", (moduleNamePath: string, moduleExports: any): View => {
    // Possible XML file path.
    const fileName = resolveFileName(moduleNamePath, "xml");

    // Attempts to implement https://github.com/NativeScript/NativeScript/issues/1311
    // if (page && fileName === `${moduleNamePath}.port.xml` || fileName === `${moduleNamePath}.land.xml`){
    //     page["isBiOrientational"] = true;
    // };

    return loadPage(moduleNamePath, fileName, moduleExports);
})

export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEntry): View => {
    if (entry.create) {
        return createView(entry);
    } else if (entry.moduleName) {
        // Current app full path.
        const currentAppPath = knownFolders.currentApp().path;
        
        // Full path of the module = current app full path + module name.
        const moduleNamePath = path.join(currentAppPath, entry.moduleName);
        const moduleExports = loadModule(moduleNamePath, entry);

        if (moduleExports && moduleExports.createPage) {
            return moduleCreateView(moduleNamePath, moduleExports);
        } else {
            // cssFileName is loaded inside pageFromBuilder->loadPage
            return viewFromBuilder(moduleNamePath, moduleExports);
        }
    }
    
    throw new Error("Failed to load page XML file for module: " + entry.moduleName);
});

const createView = profile("entry.create", (entry: ViewEntry): View => {
    const view = entry.create();
    if (!view) {
        throw new Error("Failed to create Page with entry.create() function.");
    }

    return view;
});

interface ModuleExports {
    createPage?: () => View;
}

const moduleCreateView = profile("module.createView", (moduleNamePath: string, moduleExports: ModuleExports): View => {
    const view = moduleExports.createPage();
    const cssFileName = resolveFileName(moduleNamePath, "css");
    
    // If there is no cssFile only appCss will be applied at loaded.
    if (cssFileName) {
        view.addCssFile(cssFileName);
    }
    return view;
});

function loadInternal(fileName: string, context?: any, moduleNamePath?: string): ComponentModule {
    let componentModule: ComponentModule;

    const appPath = knownFolders.currentApp().path;
    const filePathRelativeToApp = (moduleNamePath && moduleNamePath.startsWith(appPath) ? "./" + moduleNamePath.substr(appPath.length + 1) : moduleNamePath) + ".xml";

    if (global.moduleExists(filePathRelativeToApp)) {
        var text = global.loadModule(filePathRelativeToApp);
        componentModule = parseInternal(text, context, fileName, moduleNamePath);
    } else if (fileName && File.exists(fileName)) {
        const file = File.fromPath(fileName);
        const text = file.readTextSync((error) => { throw new Error("Error loading file " + fileName + " :" + error.message) });
        componentModule = parseInternal(text, context, fileName, moduleNamePath);
    }

    if (componentModule && componentModule.component) {
        // Save exports to root component (will be used for templates).
        (<any>componentModule.component).exports = context;
    }

{'fraction_non_alphanumeric': 0.12288169624929725, 'fraction_numerical': 0.0013653521805477471, 'mean_word_length': 3.4775260697590795, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 35, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 12}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2257586', 'n_tokens_mistral': 3301, 'n_tokens_neox': 3302, 'n_words': 897}