@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
201 lines • 9.16 kB
JavaScript
// Types.
import { debug } from '../../utils/debug';
import { isDefined } from '../../utils/types';
import { sanitizeModuleName } from '../../utils/common';
import { setPropertyValue, getComponentModule } from './component-builder';
import { platformNames } from '../../platform';
import { resolveModuleName } from '../../module-name-resolver';
import { xml2ui } from './xml2ui';
export const ios = platformNames.ios.toLowerCase();
export const android = platformNames.android.toLowerCase();
export const defaultNameSpaceMatcher = /tns\.xsd$/i;
export class Builder {
static createViewFromEntry(entry) {
if (entry.create) {
const view = entry.create();
if (!view) {
throw new Error('Failed to create View with entry.create() function.');
}
return view;
}
else if (entry.moduleName) {
const moduleName = sanitizeModuleName(entry.moduleName);
const resolvedCodeModuleName = resolveModuleName(moduleName, ''); //`${moduleName}.xml`;
const moduleExports = resolvedCodeModuleName ? global.loadModule(resolvedCodeModuleName, true) : null;
if (moduleExports && moduleExports.createPage) {
// Exports has a createPage() method
const view = moduleExports.createPage();
const resolvedCssModuleName = resolveModuleName(moduleName, 'css'); //entry.moduleName + ".css";
if (resolvedCssModuleName) {
view.addCssFile(resolvedCssModuleName);
}
return view;
}
else {
let componentView;
if (__UI_USE_XML_PARSER__) {
const componentModule = loadInternal(moduleName, moduleExports);
componentView = componentModule && componentModule.component;
}
else {
const resolvedXmlModuleName = resolveModuleName(moduleName, 'xml');
const componentModule = resolvedXmlModuleName ? global.loadModule(resolvedXmlModuleName, true) : null;
if (componentModule?.default) {
componentView = new componentModule.default();
}
else {
throw new Error('Failed to load component from module: ' + moduleName);
}
}
return componentView;
}
}
throw new Error('Failed to load page XML file for module: ' + entry.moduleName);
}
static parse(value, context) {
if (typeof value === 'function') {
return value();
}
else {
const exports = context ? getExports(context) : undefined;
const componentModule = parseInternal(value, exports);
return componentModule && componentModule.component;
}
}
static load(pathOrOptions, context) {
let componentModule;
if (typeof pathOrOptions === 'string') {
const moduleName = sanitizeModuleName(pathOrOptions);
componentModule = loadInternal(moduleName, context);
}
else {
componentModule = loadCustomComponent(pathOrOptions.path, pathOrOptions.name, pathOrOptions.attributes, pathOrOptions.exports, pathOrOptions.page, true);
}
return componentModule && componentModule.component;
}
static parseMultipleTemplates(value, context) {
const dummyComponent = `<ListView><ListView.itemTemplates>${value}</ListView.itemTemplates></ListView>`;
return parseInternal(dummyComponent, context).component['itemTemplates'];
}
}
// ui plugin developers can add to these to define their own custom types if needed
Builder.knownTemplates = new Set(['itemTemplate']);
Builder.knownMultiTemplates = new Set(['itemTemplates']);
Builder.knownCollections = new Set(['items', 'spans', 'actionItems']);
export function parse(value, context) {
console.log('parse() is deprecated. Use Builder.parse() instead.');
return Builder.parse(value, context);
}
export function parseMultipleTemplates(value, context) {
console.log('parseMultipleTemplates() is deprecated. Use Builder.parseMultipleTemplates() instead.');
return Builder.parseMultipleTemplates(value, context);
}
export function load(pathOrOptions, context) {
console.log('load() is deprecated. Use Builder.load() instead.');
return Builder.load(pathOrOptions, context);
}
export function createViewFromEntry(entry) {
console.log('createViewFromEntry() is deprecated. Use Builder.createViewFromEntry() instead.');
return Builder.createViewFromEntry(entry);
}
function loadInternal(moduleName, moduleExports) {
let componentModule;
const resolvedXmlModule = resolveModuleName(moduleName, 'xml');
if (resolvedXmlModule) {
const text = global.loadModule(resolvedXmlModule, true);
componentModule = parseInternal(text, moduleExports, resolvedXmlModule, moduleName);
}
const componentView = componentModule && componentModule.component;
if (componentView) {
// Save exports to root component (will be used for templates).
componentView.exports = moduleExports;
// Save _moduleName - used for livesync
componentView._moduleName = moduleName;
}
if (!componentModule) {
throw new Error('Failed to load component from module: ' + moduleName);
}
return componentModule;
}
export function loadCustomComponent(componentNamespace, componentName, attributes, context, parentPage, isRootComponent = true, moduleNamePath) {
if (!parentPage && context) {
// Read the parent page that was passed down below
// https://github.com/NativeScript/NativeScript/issues/1639
parentPage = context['_parentPage'];
delete context['_parentPage'];
}
let result;
componentNamespace = sanitizeModuleName(componentNamespace);
const moduleName = `${componentNamespace}/${componentName}`;
const resolvedCodeModuleName = resolveModuleName(moduleName, '');
const resolvedXmlModuleName = resolveModuleName(moduleName, 'xml');
let resolvedCssModuleName = resolveModuleName(moduleName, 'css');
if (resolvedXmlModuleName) {
// Custom components with XML
let subExports = context;
if (resolvedCodeModuleName) {
// Component has registered code module.
subExports = global.loadModule(resolvedCodeModuleName, true);
}
// Pass the parent page down the chain in case of custom components nested on many levels. Use the context for piggybacking.
// https://github.com/NativeScript/NativeScript/issues/1639
if (!subExports) {
subExports = {};
}
subExports['_parentPage'] = parentPage;
result = loadInternal(moduleName, subExports);
// Attributes will be transferred to the custom component
if (isDefined(result) && isDefined(result.component) && isDefined(attributes)) {
for (const attr in attributes) {
setPropertyValue(result.component, subExports, context, attr, attributes[attr]);
}
}
}
else {
// Custom components without XML
result = getComponentModule(componentName, componentNamespace, attributes, context, moduleNamePath, isRootComponent);
// The namespace is the JS module and the (componentName is the name of the class in the module)
// So if there is no componentNamespace/componentName.{qualifiers}.css we should also look for
// componentNamespace.{qualifiers}.css
if (!resolvedCssModuleName) {
resolvedCssModuleName = resolveModuleName(componentNamespace, 'css');
}
}
// Add CSS from webpack module if exists.
if (parentPage && resolvedCssModuleName) {
parentPage.addCssFile(resolvedCssModuleName);
}
return result;
}
export function getExports(instance) {
const isView = !!instance._domId;
if (!isView) {
return instance.exports || instance;
}
let exportObject = instance.exports;
let parent = instance.parent;
while (exportObject === undefined && parent) {
exportObject = parent.exports;
parent = parent.parent;
}
return exportObject;
}
function parseInternal(value, context, xmlModule, moduleName) {
if (__UI_USE_XML_PARSER__) {
let start;
let ui;
const errorFormat = debug && xmlModule ? xml2ui.SourceErrorFormat(xmlModule) : xml2ui.PositionErrorFormat;
const componentSourceTracker = debug && xmlModule
? xml2ui.ComponentSourceTracker(xmlModule)
: () => {
// no-op
};
(start = new xml2ui.XmlStringParser(errorFormat)).pipe(new xml2ui.PlatformFilter()).pipe(new xml2ui.XmlStateParser((ui = new xml2ui.ComponentParser(context, errorFormat, componentSourceTracker, moduleName))));
start.parse(value);
return ui.rootComponentModule;
}
else {
return null;
}
}
//# sourceMappingURL=index.js.map