mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
feat: Scoped Packages (#7911)
* chore: move tns-core-modules to nativescript-core * chore: preparing compat generate script * chore: add missing definitions * chore: no need for http-request to be private * chore: packages chore * test: generate tests for tns-core-modules * chore: add anroid module for consistency * chore: add .npmignore * chore: added privateModulesWhitelist * chore(webpack): added bundle-entry-points * chore: scripts * chore: tests changed to use @ns/core * test: add scoped-packages test project * test: fix types * test: update test project * chore: build scripts * chore: update build script * chore: npm scripts cleanup * chore: make the compat pgk work with old wp config * test: generate diff friendly tests * chore: create barrel exports * chore: move files after rebase * chore: typedoc config * chore: compat mode * chore: review of barrels * chore: remove tns-core-modules import after rebase * chore: dev workflow setup * chore: update developer-workflow * docs: experiment with API extractor * chore: api-extractor and barrel exports * chore: api-extractor configs * chore: generate d.ts rollup with api-extractor * refactor: move methods inside Frame * chore: fic tests to use Frame static methods * refactor: create Builder class * refactor: use Builder class in tests * refactor: include Style in ui barrel * chore: separate compat build script * chore: fix tslint errors * chore: update NATIVESCRIPT_CORE_ARGS * chore: fix compat pack * chore: fix ui-test-app build with linked modules * chore: Application, ApplicationSettings, Connectivity and Http * chore: export Trace, Profiling and Utils * refactor: Static create methods for ImageSource * chore: fix deprecated usages of ImageSource * chore: move Span and FormattedString to ui * chore: add events-args and ImageSource to index files * chore: check for CLI >= 6.2 when building for IOS * chore: update travis build * chore: copy Pod file to compat package * chore: update error msg ui-tests-app * refactor: Apply suggestions from code review Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com> * chore: typings and refs * chore: add missing d.ts files for public API * chore: adress code review FB * chore: update api-report * chore: dev-workflow for other apps * chore: api update * chore: update api-report
This commit is contained in:

committed by
GitHub

parent
6c7139477e
commit
cc97a16800
@ -0,0 +1,208 @@
|
||||
// Definitions.
|
||||
import { ComponentModule } from ".";
|
||||
import { View } from "../../core/view";
|
||||
|
||||
// Types.
|
||||
import { isEventOrGesture } from "../../core/bindable";
|
||||
import { getBindingOptions, bindingConstants } from "../binding-builder";
|
||||
import { profile } from "../../../profiling";
|
||||
import * as debugModule from "../../../utils/debug";
|
||||
import * as platform from "../../../platform";
|
||||
import { sanitizeModuleName } from "../module-name-sanitizer";
|
||||
import { resolveModuleName } from "../../../module-name-resolver";
|
||||
|
||||
const UI_PATH = "ui/";
|
||||
const MODULES = {
|
||||
"TabViewItem": "ui/tab-view",
|
||||
"TabStrip": "ui/tab-navigation-base/tab-strip",
|
||||
"TabStripItem": "ui/tab-navigation-base/tab-strip-item",
|
||||
"TabContentItem": "ui/tab-navigation-base/tab-content-item",
|
||||
"FormattedString": "ui/text-base/formatted-string",
|
||||
"Span": "ui/text-base/span",
|
||||
"ActionItem": "ui/action-bar",
|
||||
"NavigationButton": "ui/action-bar",
|
||||
"SegmentedBarItem": "ui/segmented-bar",
|
||||
};
|
||||
|
||||
const CODE_FILE = "codeFile";
|
||||
const CSS_FILE = "cssFile";
|
||||
const IMPORT = "import";
|
||||
|
||||
const createComponentInstance = profile("createComponentInstance", (elementName: string, namespace: string): { instance: View, instanceModule: Object } => {
|
||||
let instance: View;
|
||||
let instanceModule: Object;
|
||||
// Get module id.
|
||||
let resolvedModuleName;
|
||||
try {
|
||||
if (typeof namespace === "string") {
|
||||
resolvedModuleName = resolveModuleName(namespace, "");
|
||||
instanceModule = global.loadModule(resolvedModuleName, true);
|
||||
} else {
|
||||
// load module from tns-core-modules/ui or mapped paths
|
||||
resolvedModuleName = MODULES[elementName] || UI_PATH +
|
||||
(elementName.toLowerCase().indexOf("layout") !== -1 ? "layouts/" : "") +
|
||||
elementName.split(/(?=[A-Z])/).join("-").toLowerCase();
|
||||
|
||||
// don't register core modules for HMR self-accept
|
||||
instanceModule = global.loadModule(resolvedModuleName, false);
|
||||
}
|
||||
|
||||
// Get the component type from module.
|
||||
const instanceType = instanceModule[elementName] || Object;
|
||||
|
||||
// Create instance of the component.
|
||||
instance = new instanceType();
|
||||
} catch (ex) {
|
||||
const debug: typeof debugModule = require("../../../utils/debug");
|
||||
throw new debug.ScopeError(ex, "Module '" + resolvedModuleName + "' not found for element '" + (namespace ? namespace + ":" : "") + elementName + "'.");
|
||||
}
|
||||
|
||||
return { instance, instanceModule };
|
||||
});
|
||||
|
||||
const getComponentModuleExports = profile("getComponentModuleExports", (instance: View, moduleExports: Object, attributes: Object): Object => {
|
||||
if (attributes) {
|
||||
const codeFileAttribute = attributes[CODE_FILE] || attributes[IMPORT];
|
||||
if (codeFileAttribute) {
|
||||
const resolvedCodeFileModule = resolveModuleName(sanitizeModuleName(codeFileAttribute), "");
|
||||
if (resolvedCodeFileModule) {
|
||||
moduleExports = global.loadModule(resolvedCodeFileModule, true);
|
||||
(<any>instance).exports = moduleExports;
|
||||
} else {
|
||||
throw new Error(`Code file with path "${codeFileAttribute}" cannot be found! Looking for webpack module with name "${resolvedCodeFileModule}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return moduleExports;
|
||||
});
|
||||
|
||||
const applyComponentCss = profile("applyComponentCss", (instance: View, moduleName: string, attributes: Object) => {
|
||||
let cssApplied = false;
|
||||
|
||||
if (attributes && attributes[CSS_FILE]) {
|
||||
let resolvedCssModuleName = resolveModuleName(sanitizeModuleName(attributes[CSS_FILE]), "css");
|
||||
|
||||
if (resolvedCssModuleName) {
|
||||
instance.addCssFile(resolvedCssModuleName);
|
||||
cssApplied = true;
|
||||
} else {
|
||||
throw new Error(`Css file with path "${attributes[CSS_FILE]}" cannot be found! Looking for webpack module with name "${resolvedCssModuleName}"`);
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleName && !cssApplied) {
|
||||
let resolvedCssModuleName = resolveModuleName(moduleName, "css");
|
||||
if (resolvedCssModuleName) {
|
||||
instance.addCssFile(resolvedCssModuleName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const applyComponentAttributes = profile("applyComponentAttributes", (instance: View, instanceModule: Object, moduleExports: Object, attributes: Object) => {
|
||||
if (instance && instanceModule) {
|
||||
for (let attr in attributes) {
|
||||
|
||||
const attrValue = <string>attributes[attr];
|
||||
|
||||
if (attr.indexOf(":") !== -1) {
|
||||
const platformName = attr.split(":")[0].trim();
|
||||
|
||||
if (platformName.toLowerCase() === platform.device.os.toLowerCase()) {
|
||||
attr = attr.split(":")[1].trim();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (attr.indexOf(".") !== -1) {
|
||||
let subObj = instance;
|
||||
const properties = attr.split(".");
|
||||
const subPropName = properties[properties.length - 1];
|
||||
|
||||
for (let i = 0; i < properties.length - 1; i++) {
|
||||
if (subObj !== undefined && subObj !== null) {
|
||||
subObj = subObj[properties[i]];
|
||||
}
|
||||
}
|
||||
|
||||
if (subObj !== undefined && subObj !== null) {
|
||||
setPropertyValue(subObj, instanceModule, moduleExports, subPropName, attrValue);
|
||||
}
|
||||
} else {
|
||||
setPropertyValue(instance, instanceModule, moduleExports, attr, attrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export function getComponentModule(elementName: string, namespace: string, attributes: Object, moduleExports: Object, moduleNamePath?: string, isRootComponent?: boolean): ComponentModule {
|
||||
// Support lower-case-dashed component declaration in the XML (https://github.com/NativeScript/NativeScript/issues/309).
|
||||
elementName = elementName.split("-").map(s => s[0].toUpperCase() + s.substring(1)).join("");
|
||||
|
||||
const { instance, instanceModule } = createComponentInstance(elementName, namespace);
|
||||
moduleExports = getComponentModuleExports(instance, moduleExports, attributes);
|
||||
if (isRootComponent) {
|
||||
applyComponentCss(instance, moduleNamePath, attributes);
|
||||
}
|
||||
|
||||
applyComponentAttributes(instance, instanceModule, moduleExports, attributes);
|
||||
|
||||
let componentModule;
|
||||
if (instance && instanceModule) {
|
||||
componentModule = { component: instance, exports: instanceModule };
|
||||
}
|
||||
|
||||
return componentModule;
|
||||
}
|
||||
|
||||
export function setPropertyValue(instance: View, instanceModule: Object, exports: Object, propertyName: string, propertyValue: any) {
|
||||
// Note: instanceModule can be null if we are loading custom component with no code-behind.
|
||||
if (isBinding(propertyValue) && instance.bind) {
|
||||
const bindOptions = getBindingOptions(propertyName, getBindingExpressionFromAttribute(propertyValue));
|
||||
instance.bind({
|
||||
sourceProperty: bindOptions[bindingConstants.sourceProperty],
|
||||
targetProperty: bindOptions[bindingConstants.targetProperty],
|
||||
expression: bindOptions[bindingConstants.expression],
|
||||
twoWay: bindOptions[bindingConstants.twoWay]
|
||||
}, bindOptions[bindingConstants.source]);
|
||||
}
|
||||
else if (isEventOrGesture(propertyName, instance)) {
|
||||
// Get the event handler from page module exports.
|
||||
const handler = exports && exports[propertyValue];
|
||||
|
||||
// Check if the handler is function and add it to the instance for specified event name.
|
||||
if (typeof handler === "function") {
|
||||
instance.on(propertyName, handler);
|
||||
}
|
||||
}
|
||||
else if (isKnownFunction(propertyName, instance) && exports && typeof exports[propertyValue] === "function") {
|
||||
instance[propertyName] = exports[propertyValue];
|
||||
}
|
||||
else {
|
||||
instance[propertyName] = propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
function getBindingExpressionFromAttribute(value: string): string {
|
||||
return value.replace("{{", "").replace("}}", "").trim();
|
||||
}
|
||||
|
||||
function isBinding(value: any): boolean {
|
||||
let isBinding;
|
||||
|
||||
if (typeof value === "string") {
|
||||
const str = value.trim();
|
||||
isBinding = str.indexOf("{{") === 0 && str.lastIndexOf("}}") === str.length - 2;
|
||||
}
|
||||
|
||||
return isBinding;
|
||||
}
|
||||
|
||||
// For example, ListView.itemTemplateSelector
|
||||
let KNOWN_FUNCTIONS = "knownFunctions";
|
||||
function isKnownFunction(name: string, instance: View): boolean {
|
||||
return instance.constructor
|
||||
&& KNOWN_FUNCTIONS in instance.constructor
|
||||
&& instance.constructor[KNOWN_FUNCTIONS].indexOf(name) !== -1;
|
||||
}
|
Reference in New Issue
Block a user