refactor: HMR and webpack improvements (#7462)
* chore: update project * refactor: clear module-name cache on orientation * feat: add custom component in qualifiers app * feat: enable HMR for custom components * refactor: remove redundant check * chore: clean console.log


@ -2,13 +2,17 @@
|
||||
|
||||
// Uncomment to add recyclerview-v7 dependency
|
||||
//dependencies {
|
||||
// compile 'com.android.support:recyclerview-v7:+'
|
||||
// implementation 'com.android.support:recyclerview-v7:+'
|
||||
//}
|
||||
|
||||
// If you want to add something to be applied before applying plugins' include.gradle files
|
||||
// e.g. project.ext.googlePlayServicesVersion = "15.0.1"
|
||||
// create a file named before-plugins.gradle in the current directory and place it there
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdkVersion 17
|
||||
generatedDensities = []
|
||||
applicationId = "org.nativescript.animation"
|
||||
}
|
||||
aaptOptions {
|
||||
additionalParameters "--no-version-vectors"
|
||||
|
@ -10,10 +10,6 @@
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true"/>
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="17"
|
||||
android:targetSdkVersion="__APILEVEL__"/>
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
@ -28,7 +24,7 @@
|
||||
<activity
|
||||
android:name="com.tns.NativeScriptActivity"
|
||||
android:label="@string/title_activity_kimera"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|locale|uiMode"
|
||||
android:theme="@style/LaunchScreenTheme">
|
||||
|
||||
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
|
After Width: | Height: | Size: 915 B |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 669 B |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 193 KiB |
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">TestApp</string>
|
||||
<string name="title_activity_kimera">TestApp</string>
|
||||
</resources>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-path name="external_files" path="."/>
|
||||
</paths>
|
@ -1,5 +1,4 @@
|
||||
.root-footer {
|
||||
horizontal-align: middle;
|
||||
color: black;
|
||||
background-color: lightgreen
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
.root-footer {
|
||||
horizontal-align: middle;
|
||||
color: black;
|
||||
background-color: lightpink;
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
import * as application from "tns-core-modules/application";
|
||||
import { setCategories, categories, enable } from "tns-core-modules/trace";
|
||||
|
||||
setCategories(categories.Livesync);
|
||||
enable();
|
||||
|
||||
application.run({ moduleName: "app-root" });
|
||||
|
5
e2e/file-qualifiers/app/components/my-component.css
Normal file
@ -0,0 +1,5 @@
|
||||
.custom-component {
|
||||
color: navy;
|
||||
font-weight: bold;
|
||||
font-size: 20;
|
||||
}
|
5
e2e/file-qualifiers/app/components/my-component.land.css
Normal file
@ -0,0 +1,5 @@
|
||||
.custom-component {
|
||||
color: darkolivegreen;
|
||||
font-weight: bold;
|
||||
font-size: 20;
|
||||
}
|
3
e2e/file-qualifiers/app/components/my-component.land.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function onLoaded() {
|
||||
console.log("---> Custom component LANDSCAPE loaded");
|
||||
}
|
3
e2e/file-qualifiers/app/components/my-component.land.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<StackLayout class="p-20" loaded="onLoaded">
|
||||
<Label text="CUSTOM COMPONENT - LANDSCAPE" class="custom-component" />
|
||||
</StackLayout>
|
3
e2e/file-qualifiers/app/components/my-component.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function onLoaded() {
|
||||
console.log("---> Custom component DEFAULT loaded");
|
||||
}
|
3
e2e/file-qualifiers/app/components/my-component.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<StackLayout class="p-20" loaded="onLoaded">
|
||||
<Label text="CUSTOM COMPONENT - DEFAULT" class="custom-component" />
|
||||
</StackLayout>
|
3
e2e/file-qualifiers/app/main/main-page-vm.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { fromObject } from "tns-core-modules/data/observable";
|
||||
|
||||
export const vm = fromObject({ prop: "property from VM" });
|
@ -1,7 +1,17 @@
|
||||
export function onNavigatedTo(args) {
|
||||
console.log("---> [LANDSCAPE] onNavigatedTo");
|
||||
import { topmost } from "tns-core-modules/ui/frame";
|
||||
import { vm } from "./main-page-vm";
|
||||
|
||||
export function onNavigatingTo(args) {
|
||||
console.log("---> [LANDSCAPE] onNavigatingTo");
|
||||
args.object.page.bindingContext = vm;
|
||||
}
|
||||
|
||||
export function tap(args) {
|
||||
console.log("---> [LANDSCAPE] tap");
|
||||
}
|
||||
|
||||
export function navigate(args) {
|
||||
console.log("---> [LANDSCAPE] navigate");
|
||||
|
||||
topmost().navigate("other/other-page");
|
||||
}
|
@ -1,7 +1,13 @@
|
||||
<Page navigatedTo="onNavigatedTo">
|
||||
<Page navigatingTo="onNavigatingTo" xmlns:cc="components">
|
||||
<StackLayout class="page-content">
|
||||
<Label text="This is LANDSACPE page" />
|
||||
|
||||
<Button text="hey there" tap="navigate" />
|
||||
<Button text="tap" tap="tap" />
|
||||
|
||||
<Button text="navigate" tap="navigate" />
|
||||
|
||||
<Label text="{{ prop }}" />
|
||||
|
||||
<cc:my-component />
|
||||
</StackLayout>
|
||||
</Page>
|
||||
|
@ -1,7 +1,17 @@
|
||||
export function onNavigatedTo(args) {
|
||||
console.log("---> [DEFAULT] onNavigatedTo");
|
||||
import { topmost } from "tns-core-modules/ui/frame";
|
||||
import { vm } from "./main-page-vm";
|
||||
|
||||
export function onNavigatingTo(args) {
|
||||
console.log("---> [DEFAULT] onNavigatingTo");
|
||||
args.object.page.bindingContext = vm;
|
||||
}
|
||||
|
||||
export function tap(args) {
|
||||
console.log("---> [DEFAULT] tap");
|
||||
}
|
||||
|
||||
export function navigate(args) {
|
||||
console.log("---> [DEFAULT] navigate");
|
||||
|
||||
topmost().navigate("other/other-page");
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
<Page navigatedTo="onNavigatedTo">
|
||||
<Page navigatingTo="onNavigatingTo" xmlns:cc="components">
|
||||
<StackLayout class="page-content">
|
||||
<Label text="This is DEFAULT page" />
|
||||
|
||||
<Button text="hey there" tap="navigate" />
|
||||
<Button text="tap" tap="tap" />
|
||||
|
||||
<Button text="navigate" tap="navigate" />
|
||||
|
||||
<Label text="{{ prop }}" />
|
||||
|
||||
<cc:my-component />
|
||||
</StackLayout>
|
||||
</Page>
|
||||
|
4
e2e/file-qualifiers/app/other/other-page.css
Normal file
@ -0,0 +1,4 @@
|
||||
.page-content {
|
||||
padding: 20;
|
||||
background-color: lightblue;
|
||||
}
|
4
e2e/file-qualifiers/app/other/other-page.land.css
Normal file
@ -0,0 +1,4 @@
|
||||
.page-content {
|
||||
padding: 20;
|
||||
background-color: lightcoral;
|
||||
}
|
15
e2e/file-qualifiers/app/other/other-page.land.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { topmost } from "tns-core-modules/ui/frame";
|
||||
|
||||
export function onNavigatedTo(args) {
|
||||
console.log("---> [LANDSCAPE other] onNavigatedTo");
|
||||
}
|
||||
|
||||
export function tap(args) {
|
||||
console.log("---> [LANDSCAPE other] tap");
|
||||
}
|
||||
|
||||
export function navigate(args) {
|
||||
console.log("---> [LANDSCAPE other] navigate");
|
||||
|
||||
topmost().navigate("main/main-page");
|
||||
}
|
12
e2e/file-qualifiers/app/other/other-page.land.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<Page navigatedTo="onNavigatedTo" xmlns:cc="components">
|
||||
<StackLayout class="page-content">
|
||||
<Label text="This is LANDSACPE other page" />
|
||||
|
||||
<Button text="tap" tap="tap" />
|
||||
|
||||
<Button text="navigate" tap="navigate" />
|
||||
|
||||
<cc:my-component />
|
||||
|
||||
</StackLayout>
|
||||
</Page>
|
15
e2e/file-qualifiers/app/other/other-page.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { topmost } from "tns-core-modules/ui/frame";
|
||||
|
||||
export function onNavigatedTo(args) {
|
||||
console.log("---> [DEFAULT other] onNavigatedTo");
|
||||
}
|
||||
|
||||
export function tap(args) {
|
||||
console.log("---> [DEFAULT other] tap");
|
||||
}
|
||||
|
||||
export function navigate(args) {
|
||||
console.log("---> [DEFAULT other] navigate");
|
||||
|
||||
topmost().navigate("main/main-page");
|
||||
}
|
11
e2e/file-qualifiers/app/other/other-page.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<Page navigatedTo="onNavigatedTo" xmlns:cc="components">
|
||||
<StackLayout class="page-content">
|
||||
<Label text="This is DEFAULT other page" />
|
||||
|
||||
<Button text="tap" tap="tap" />
|
||||
|
||||
<Button text="navigate" tap="navigate" />
|
||||
|
||||
<cc:my-component />
|
||||
</StackLayout>
|
||||
</Page>
|
@ -2,7 +2,5 @@
|
||||
"android": {
|
||||
"v8Flags": "--expose_gc"
|
||||
},
|
||||
"main": "app.js",
|
||||
"name": "tns-template-hello-world-ts",
|
||||
"version": "3.4.0"
|
||||
"main": "app.js"
|
||||
}
|
@ -179,25 +179,14 @@ module.exports = env => {
|
||||
unitTesting,
|
||||
appFullPath,
|
||||
projectRoot,
|
||||
registerModules: /(root|page|app)(\.(land|port|phone|tablet|minH\d+|minW\d+|minWH\d+))?\.(xml|css|js|ts|scss)$/ // NB: MODIFIED
|
||||
}
|
||||
},
|
||||
].filter(loader => !!loader)
|
||||
},
|
||||
|
||||
{
|
||||
test: /(page|app)(\.(land|port|phone|tablet|minH\d+|minW\d+|minWH\d+))?\.ts$/,
|
||||
use: "nativescript-dev-webpack/script-hot-loader"
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(css|scss)$/,
|
||||
use: "nativescript-dev-webpack/style-hot-loader"
|
||||
},
|
||||
|
||||
{
|
||||
test: /\.(html|xml)$/,
|
||||
use: "nativescript-dev-webpack/markup-hot-loader"
|
||||
test: /\.(ts|css|scss|less|html|xml)$/,
|
||||
use: "nativescript-dev-webpack/hmr/hot-loader"
|
||||
},
|
||||
|
||||
{ test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader" },
|
||||
|
@ -153,7 +153,7 @@ export function test_loadWithAttributes() {
|
||||
}
|
||||
|
||||
export function test_parse_ShouldNotCrashWithoutExports() {
|
||||
const xml = global.loadModule("xml-declaration/mainPage.xml");
|
||||
const xml = global.loadModule("xml-declaration/mainPage.xml", true);
|
||||
|
||||
var v: view.View = builder.parse(xml);
|
||||
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
|
||||
@ -831,7 +831,7 @@ export function test_NonExistingElementInTemplateError() {
|
||||
}
|
||||
|
||||
export function test_EventInTemplate() {
|
||||
var pageCode = global.loadModule("xml-declaration/template-builder-tests/event-in-template");
|
||||
var pageCode = global.loadModule("xml-declaration/template-builder-tests/event-in-template", true);
|
||||
var notified = false;
|
||||
pageCode.test = (args) => {
|
||||
notified = true;
|
||||
@ -854,7 +854,7 @@ export function test_EventInTemplate() {
|
||||
}
|
||||
|
||||
export function test_EventInCodelessFragment() {
|
||||
var pageCode = global.loadModule("./xml-declaration/template-builder-tests/event-in-codeless-fragment");
|
||||
var pageCode = global.loadModule("./xml-declaration/template-builder-tests/event-in-codeless-fragment", true);
|
||||
|
||||
var notified = false;
|
||||
pageCode.setCallback((args) => {
|
||||
|
@ -185,7 +185,7 @@ export var test_XmlParser_NamespacesTest = function () {
|
||||
};
|
||||
|
||||
export function test_MultiParserTemplate() {
|
||||
const xml = global.loadModule("xml-parser-tests/itemTemplates.xml");
|
||||
const xml = global.loadModule("xml-parser-tests/itemTemplates.xml", true);
|
||||
|
||||
const view: any = builder.parse(xml);
|
||||
TKUnit.assertNotNull(view.items);
|
||||
|
@ -1,5 +1,7 @@
|
||||
// Required by TypeScript compiler
|
||||
require("./ts-helpers");
|
||||
import "./ts-helpers";
|
||||
|
||||
import "./register-module-helpers";
|
||||
|
||||
// This method iterates all the keys in the source exports object and copies them to the destination exports one.
|
||||
// Note: the method will not check for naming collisions and will override any already existing entries in the destination exports.
|
||||
@ -12,110 +14,6 @@ global.moduleMerge = function (sourceExports: any, destExports: any) {
|
||||
import * as timerModule from "../timer";
|
||||
import * as dialogsModule from "../ui/dialogs";
|
||||
|
||||
type ModuleLoader = (name?: string) => any;
|
||||
const modules: Map<string, ModuleLoader> = new Map<string, ModuleLoader>();
|
||||
|
||||
(<any>global).moduleResolvers = [global.require];
|
||||
|
||||
global.registerModule = function (name: string, loader: ModuleLoader): void {
|
||||
// console.log("[global.registerModule]", name);
|
||||
modules.set(name, loader);
|
||||
};
|
||||
|
||||
global._unregisterModule = function (name: string): void {
|
||||
// console.log("[global._unregisterModule]", name);
|
||||
modules.delete(name);
|
||||
};
|
||||
|
||||
interface Context {
|
||||
keys(): string[];
|
||||
(key: string): any;
|
||||
}
|
||||
interface ExtensionMap {
|
||||
[originalFileExtension: string]: string;
|
||||
}
|
||||
|
||||
const defaultExtensionMap = {
|
||||
".js": ".js",
|
||||
".ts": ".js",
|
||||
".css": ".css",
|
||||
".scss": ".css",
|
||||
".less": ".css",
|
||||
".sass": ".css",
|
||||
".xml": ".xml"
|
||||
};
|
||||
|
||||
global.registerWebpackModules = function registerWebpackModules(context: Context, extensionMap: ExtensionMap = {}) {
|
||||
context.keys().forEach(key => {
|
||||
const extDotIndex = key.lastIndexOf(".");
|
||||
const base = key.substr(0, extDotIndex);
|
||||
const originalExt = key.substr(extDotIndex);
|
||||
const registerExt = extensionMap[originalExt] || defaultExtensionMap[originalExt] || originalExt;
|
||||
|
||||
// We prefer source files for webpack scenarios before compilation leftovers,
|
||||
// e. g. if we get a .js and .ts for the same module, the .js is probably the compiled version of the .ts file,
|
||||
// so we register the .ts with higher priority, similar is the case with us preferring the .scss to .css
|
||||
const isSourceFile = originalExt !== registerExt;
|
||||
|
||||
const registerName = base + registerExt;
|
||||
if (registerName.startsWith("./") && registerName.endsWith(".js")) {
|
||||
const jsNickNames = [
|
||||
// This is extremely short version like "main-page" that was promoted to be used with global.registerModule("module-name", loaderFunc);
|
||||
registerName.substr(2, registerName.length - 5),
|
||||
// This is for supporting module names like "./main/main-page"
|
||||
registerName.substr(0, registerName.length - 3),
|
||||
// This is for supporting module names like "main/main-page.js"
|
||||
registerName.substr(2),
|
||||
];
|
||||
|
||||
jsNickNames.forEach(jsNickName => {
|
||||
if (isSourceFile || !global.moduleExists(jsNickName)) {
|
||||
global.registerModule(jsNickName, () => context(key));
|
||||
}
|
||||
});
|
||||
} else if (registerName.startsWith("./")) {
|
||||
const moduleNickNames = [
|
||||
// This is for supporting module names like "main/main-page.xml"
|
||||
registerName.substr(2),
|
||||
];
|
||||
|
||||
moduleNickNames.forEach(moduleNickName => {
|
||||
if (!global.moduleExists(moduleNickName)) {
|
||||
global.registerModule(moduleNickName, () => context(key));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isSourceFile || !global.moduleExists(registerName)) {
|
||||
global.registerModule(registerName, () => context(key));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
global.moduleExists = function (name: string): boolean {
|
||||
return modules.has(name);
|
||||
};
|
||||
|
||||
global.loadModule = function (name: string): any {
|
||||
const loader = modules.get(name);
|
||||
if (loader) {
|
||||
return loader(name);
|
||||
}
|
||||
|
||||
for (let resolver of (<any>global).moduleResolvers) {
|
||||
const result = resolver(name);
|
||||
if (result) {
|
||||
modules.set(name, () => result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
global.getRegisteredModules = function (): string[] {
|
||||
return Array.from(modules.keys());
|
||||
};
|
||||
|
||||
global.zonedCallback = function (callback: Function): Function {
|
||||
if ((<any>global).zone) {
|
||||
// Zone v0.5.* style callback wrapping
|
||||
|
127
tns-core-modules/globals/register-module-helpers.ts
Normal file
@ -0,0 +1,127 @@
|
||||
type ModuleLoader = (name?: string) => any;
|
||||
|
||||
interface Context {
|
||||
keys(): string[];
|
||||
(key: string): any;
|
||||
}
|
||||
|
||||
interface ExtensionMap {
|
||||
[originalFileExtension: string]: string;
|
||||
}
|
||||
|
||||
const modules: Map<string, { moduleId: string, loader: ModuleLoader }> = new Map<string, { moduleId: string, loader: ModuleLoader }>();
|
||||
const modulesLoadedForUI = new Set<string>();
|
||||
const defaultExtensionMap: ExtensionMap = {
|
||||
".js": ".js",
|
||||
".ts": ".js",
|
||||
".css": ".css",
|
||||
".scss": ".css",
|
||||
".less": ".css",
|
||||
".sass": ".css",
|
||||
".xml": ".xml"
|
||||
};
|
||||
|
||||
// Cast to <any> because moduleResolvers is read-only in definitions
|
||||
(<any>global).moduleResolvers = [global.require];
|
||||
|
||||
global.registerModule = function (name: string, loader: ModuleLoader): void {
|
||||
modules.set(name, { loader, moduleId: name });
|
||||
};
|
||||
|
||||
global._unregisterModule = function _unregisterModule(name: string): void {
|
||||
modules.delete(name);
|
||||
};
|
||||
|
||||
global._isModuleLoadedForUI = function _isModuleLoadedForUI(moduleName: string): boolean {
|
||||
return modulesLoadedForUI.has(moduleName);
|
||||
};
|
||||
|
||||
global.registerWebpackModules = function registerWebpackModules(context: Context, extensionMap: ExtensionMap = {}) {
|
||||
context.keys().forEach(moduleId => {
|
||||
const extDotIndex = moduleId.lastIndexOf(".");
|
||||
const base = moduleId.substr(0, extDotIndex);
|
||||
const originalExt = moduleId.substr(extDotIndex);
|
||||
const registerExt = extensionMap[originalExt] || defaultExtensionMap[originalExt] || originalExt;
|
||||
|
||||
// We prefer source files for webpack scenarios before compilation leftovers,
|
||||
// e. g. if we get a .js and .ts for the same module, the .js is probably the compiled version of the .ts file,
|
||||
// so we register the .ts with higher priority, similar is the case with us preferring the .scss to .css
|
||||
const isSourceFile = originalExt !== registerExt;
|
||||
const registerName = base + registerExt;
|
||||
|
||||
const registerWithName = (nickName: string) => {
|
||||
modules.set(nickName, {
|
||||
moduleId,
|
||||
loader: () => {
|
||||
return context(moduleId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (registerName.startsWith("./") && registerName.endsWith(".js")) {
|
||||
const jsNickNames = [
|
||||
// This is extremely short version like "main-page" that was promoted to be used with global.registerModule("module-name", loaderFunc);
|
||||
registerName.substr(2, registerName.length - 5),
|
||||
// This is for supporting module names like "./main/main-page"
|
||||
registerName.substr(0, registerName.length - 3),
|
||||
// This is for supporting module names like "main/main-page.js"
|
||||
registerName.substr(2),
|
||||
];
|
||||
|
||||
jsNickNames.forEach(jsNickName => {
|
||||
if (isSourceFile || !global.moduleExists(jsNickName)) {
|
||||
registerWithName(jsNickName);
|
||||
}
|
||||
});
|
||||
} else if (registerName.startsWith("./")) {
|
||||
const moduleNickNames = [
|
||||
// This is for supporting module names like "main/main-page.xml"
|
||||
registerName.substr(2),
|
||||
];
|
||||
|
||||
moduleNickNames.forEach(moduleNickName => {
|
||||
if (!global.moduleExists(moduleNickName)) {
|
||||
registerWithName(moduleNickName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isSourceFile || !global.moduleExists(registerName)) {
|
||||
registerWithName(registerName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
global.moduleExists = function moduleExists(name: string): boolean {
|
||||
return modules.has(name);
|
||||
};
|
||||
|
||||
global.loadModule = function loadModule(name: string, isUIModule: boolean = false): any {
|
||||
const moduleInfo = modules.get(name);
|
||||
if (moduleInfo) {
|
||||
if (isUIModule) {
|
||||
modulesLoadedForUI.add(moduleInfo.moduleId);
|
||||
}
|
||||
|
||||
const result = moduleInfo.loader(name);
|
||||
|
||||
if (result.enableAutoAccept) {
|
||||
result.enableAutoAccept();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (let resolver of (<any>global).moduleResolvers) {
|
||||
const result = resolver(name);
|
||||
if (result) {
|
||||
modules.set(name, { moduleId: name, loader: () => result });
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
global.getRegisteredModules = function getRegisteredModules(): string[] {
|
||||
return Array.from(modules.keys());
|
||||
};
|
@ -68,3 +68,6 @@ export function _setResolver(resolver: ModuleNameResolver) {
|
||||
}
|
||||
|
||||
appCommonModule.on("livesync", args => clearCache());
|
||||
appCommonModule.on("orientationChanged", args => {
|
||||
resolverInstance = undefined;
|
||||
});
|
||||
|
32
tns-core-modules/module.d.ts
vendored
@ -14,12 +14,14 @@ declare namespace NodeJS {
|
||||
interface Global {
|
||||
android?: any;
|
||||
require(id: string): any;
|
||||
|
||||
moduleMerge(sourceExports: any, destExports: any): void;
|
||||
|
||||
registerModule(name: string, loader: ((name: string) => any)): void;
|
||||
_unregisterModule(name: string): void;
|
||||
/**
|
||||
* Register all modules from a webpack context.
|
||||
* The context is one created using the following webpack utility:
|
||||
* https://webpack.github.io/docs/context.html
|
||||
* https://webpack.js.org/guides/dependency-management/#requirecontext
|
||||
*
|
||||
* The extension map is optional, modules in the webpack context will have their original file extension (e.g. may be ".ts" or ".scss" etc.),
|
||||
* while the built-in module builders in {N} will look for ".js", ".css" or ".xml" files. Adding a map such as:
|
||||
@ -27,9 +29,10 @@ declare namespace NodeJS {
|
||||
* { ".ts": ".js" }
|
||||
* ```
|
||||
* Will resolve lookups for .js to the .ts file.
|
||||
* By default scss, and ts files are mapped.
|
||||
* By default scss and ts files are mapped.
|
||||
*/
|
||||
registerWebpackModules(context: { keys(): string[], (key: string): any }, extensionMap?: { [originalFileExtension: string]: string });
|
||||
|
||||
/**
|
||||
* The NativeScript XML builder, style-scope, application modules use various resources such as:
|
||||
* app.css, page.xml files and modules during the application life-cycle.
|
||||
@ -43,15 +46,34 @@ declare namespace NodeJS {
|
||||
* By default the only member of the array is global.require, as last resort - if it fails to find a module it will throw.
|
||||
*/
|
||||
readonly moduleResolvers: ModuleResolver[];
|
||||
loadModule(name: string): any;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name Name of the module to be loaded
|
||||
* @param loadForUI Is this UI module is being loaded for UI from tns-core-modules/builder.
|
||||
* Xml, css/scss and js/ts modules for pages and custom-components should load with loadForUI=true.
|
||||
* Passing "true" will enable the HMR mechanics this module. Default value is false.
|
||||
*/
|
||||
loadModule(name: string, loadForUI? : boolean): any;
|
||||
|
||||
/**
|
||||
* Checks if the module has been registered with `registerModule` or in `registerWebpackModules`
|
||||
* @param name Name of the module
|
||||
*/
|
||||
moduleExists(name: string): boolean;
|
||||
moduleMerge(sourceExports: any, destExports: any): void;
|
||||
|
||||
getRegisteredModules(): string[];
|
||||
|
||||
_unregisterModule(name: string): void;
|
||||
|
||||
_isModuleLoadedForUI(moduleName: string): boolean;
|
||||
|
||||
onGlobalLayoutListener: any;
|
||||
zonedCallback(callback: Function): Function;
|
||||
Reflect?: any;
|
||||
Deprecated(target: Object, key?: string | symbol, descriptor?: any): any;
|
||||
Experimental(target: Object, key?: string | symbol, descriptor?: any): any;
|
||||
|
||||
__native?: any;
|
||||
__inspector?: any;
|
||||
__extends: any;
|
||||
|
@ -59,7 +59,7 @@ export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEn
|
||||
} else if (entry.moduleName) {
|
||||
const moduleName = sanitizeModuleName(entry.moduleName);
|
||||
const resolvedCodeModuleName = resolveModuleName(moduleName, ""); //`${moduleName}.xml`;
|
||||
let moduleExports = resolvedCodeModuleName ? global.loadModule(resolvedCodeModuleName) : null;
|
||||
let moduleExports = resolvedCodeModuleName ? global.loadModule(resolvedCodeModuleName, true) : null;
|
||||
|
||||
if (moduleExports && moduleExports.createPage) {
|
||||
// Exports has a createPage() method
|
||||
@ -87,7 +87,7 @@ function loadInternal(moduleName: string, moduleExports: any): ComponentModule {
|
||||
const resolvedXmlModule = resolveModuleName(moduleName, "xml");
|
||||
|
||||
if (resolvedXmlModule) {
|
||||
const text = global.loadModule(resolvedXmlModule);
|
||||
const text = global.loadModule(resolvedXmlModule, true);
|
||||
componentModule = parseInternal(text, moduleExports, resolvedXmlModule, moduleName);
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ function loadCustomComponent(componentNamespace: string, componentName?: string,
|
||||
let subExports = context;
|
||||
if (resolvedCodeModuleName) {
|
||||
// Component has registered code module.
|
||||
subExports = global.loadModule(resolvedCodeModuleName);
|
||||
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.
|
||||
|
@ -36,14 +36,16 @@ const createComponentInstance = profile("createComponentInstance", (elementName:
|
||||
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();
|
||||
}
|
||||
|
||||
instanceModule = global.loadModule(resolvedModuleName);
|
||||
// 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;
|
||||
@ -64,7 +66,7 @@ const getComponentModuleExports = profile("getComponentModuleExports", (instance
|
||||
if (codeFileAttribute) {
|
||||
const resolvedCodeFileModule = resolveModuleName(sanitizeModuleName(codeFileAttribute), "");
|
||||
if (resolvedCodeFileModule) {
|
||||
moduleExports = global.loadModule(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}"`);
|
||||
|
@ -192,6 +192,18 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle script/markup changes in custom components by falling back to page refresh
|
||||
if (viewMatchesModuleContext(this, context, ["markup", "script"]) &&
|
||||
this.page &&
|
||||
this.page.frame) {
|
||||
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Change Handled: Changing ${context.type} for ${this} inside ${this.page}`, traceCategories.Livesync);
|
||||
}
|
||||
|
||||
return this.page.frame._handleLivesync({ type: context.type, path: this.page._moduleName });
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { createViewFromEntry } from "../builder";
|
||||
import { profile } from "../../profiling";
|
||||
|
||||
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
||||
import { getModuleName } from "../../utils/utils";
|
||||
import { sanitizeModuleName } from "../builder/module-name-sanitizer";
|
||||
export * from "../core/view";
|
||||
|
||||
export enum NavigationType {
|
||||
@ -608,7 +608,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
|
||||
traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync);
|
||||
|
||||
this.replacePage(context);
|
||||
this.replacePage(context.path);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -653,9 +653,9 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected replacePage(context: ModuleContext): void {
|
||||
protected replacePage(pagePath: string): void {
|
||||
const currentBackstackEntry = this._currentEntry;
|
||||
const contextModuleName = getModuleName(context.path);
|
||||
const contextModuleName = sanitizeModuleName(pagePath);
|
||||
|
||||
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
||||
const newBackstackEntry: BackstackEntry = {
|
||||
|
@ -83,7 +83,7 @@ class CSSSource {
|
||||
let appRelativeUri = CSSSource.pathRelativeToApp(uri);
|
||||
|
||||
try {
|
||||
const cssOrAst = global.loadModule(appRelativeUri);
|
||||
const cssOrAst = global.loadModule(appRelativeUri, true);
|
||||
if (cssOrAst) {
|
||||
if (typeof cssOrAst === "string") {
|
||||
// raw-loader
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as types from "./types";
|
||||
import { dispatchToMainThread, isMainThread } from "./mainthread-helper";
|
||||
import { sanitizeModuleName } from "../ui/builder/module-name-sanitizer";
|
||||
|
||||
export * from "./mainthread-helper";
|
||||
|
||||
@ -35,7 +36,7 @@ export function convertString(value: any): any {
|
||||
export function getModuleName(path: string): string {
|
||||
let moduleName = path.replace("./", "");
|
||||
|
||||
return moduleName.substring(0, moduleName.lastIndexOf("."));
|
||||
return sanitizeModuleName(moduleName);
|
||||
}
|
||||
|
||||
export module layoutCommon {
|
||||
|