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
|
// Uncomment to add recyclerview-v7 dependency
|
||||||
//dependencies {
|
//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 {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
minSdkVersion 17
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
applicationId = "org.nativescript.animation"
|
|
||||||
}
|
}
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
additionalParameters "--no-version-vectors"
|
additionalParameters "--no-version-vectors"
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
android:xlargeScreens="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.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
@ -28,7 +24,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="com.tns.NativeScriptActivity"
|
android:name="com.tns.NativeScriptActivity"
|
||||||
android:label="@string/title_activity_kimera"
|
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">
|
android:theme="@style/LaunchScreenTheme">
|
||||||
|
|
||||||
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
|
<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 {
|
.root-footer {
|
||||||
horizontal-align: middle;
|
|
||||||
color: black;
|
color: black;
|
||||||
background-color: lightgreen
|
background-color: lightgreen
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
.root-footer {
|
.root-footer {
|
||||||
horizontal-align: middle;
|
|
||||||
color: black;
|
color: black;
|
||||||
background-color: lightpink;
|
background-color: lightpink;
|
||||||
}
|
}
|
@ -1,3 +1,7 @@
|
|||||||
import * as application from "tns-core-modules/application";
|
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" });
|
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) {
|
import { topmost } from "tns-core-modules/ui/frame";
|
||||||
console.log("---> [LANDSCAPE] onNavigatedTo");
|
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) {
|
export function navigate(args) {
|
||||||
console.log("---> [LANDSCAPE] navigate");
|
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">
|
<StackLayout class="page-content">
|
||||||
<Label text="This is LANDSACPE page" />
|
<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>
|
</StackLayout>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
export function onNavigatedTo(args) {
|
import { topmost } from "tns-core-modules/ui/frame";
|
||||||
console.log("---> [DEFAULT] onNavigatedTo");
|
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) {
|
export function navigate(args) {
|
||||||
console.log("---> [DEFAULT] navigate");
|
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">
|
<StackLayout class="page-content">
|
||||||
<Label text="This is DEFAULT page" />
|
<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>
|
</StackLayout>
|
||||||
</Page>
|
</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": {
|
"android": {
|
||||||
"v8Flags": "--expose_gc"
|
"v8Flags": "--expose_gc"
|
||||||
},
|
},
|
||||||
"main": "app.js",
|
"main": "app.js"
|
||||||
"name": "tns-template-hello-world-ts",
|
|
||||||
"version": "3.4.0"
|
|
||||||
}
|
}
|
@ -179,25 +179,14 @@ module.exports = env => {
|
|||||||
unitTesting,
|
unitTesting,
|
||||||
appFullPath,
|
appFullPath,
|
||||||
projectRoot,
|
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)
|
].filter(loader => !!loader)
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
test: /(page|app)(\.(land|port|phone|tablet|minH\d+|minW\d+|minWH\d+))?\.ts$/,
|
test: /\.(ts|css|scss|less|html|xml)$/,
|
||||||
use: "nativescript-dev-webpack/script-hot-loader"
|
use: "nativescript-dev-webpack/hmr/hot-loader"
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
test: /\.(css|scss)$/,
|
|
||||||
use: "nativescript-dev-webpack/style-hot-loader"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
test: /\.(html|xml)$/,
|
|
||||||
use: "nativescript-dev-webpack/markup-hot-loader"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{ test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader" },
|
{ test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader" },
|
||||||
|
@ -153,7 +153,7 @@ export function test_loadWithAttributes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function test_parse_ShouldNotCrashWithoutExports() {
|
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);
|
var v: view.View = builder.parse(xml);
|
||||||
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
|
TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";");
|
||||||
@ -831,7 +831,7 @@ export function test_NonExistingElementInTemplateError() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function test_EventInTemplate() {
|
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;
|
var notified = false;
|
||||||
pageCode.test = (args) => {
|
pageCode.test = (args) => {
|
||||||
notified = true;
|
notified = true;
|
||||||
@ -854,7 +854,7 @@ export function test_EventInTemplate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function test_EventInCodelessFragment() {
|
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;
|
var notified = false;
|
||||||
pageCode.setCallback((args) => {
|
pageCode.setCallback((args) => {
|
||||||
|
@ -185,7 +185,7 @@ export var test_XmlParser_NamespacesTest = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function test_MultiParserTemplate() {
|
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);
|
const view: any = builder.parse(xml);
|
||||||
TKUnit.assertNotNull(view.items);
|
TKUnit.assertNotNull(view.items);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// Required by TypeScript compiler
|
// 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.
|
// 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.
|
// 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 timerModule from "../timer";
|
||||||
import * as dialogsModule from "../ui/dialogs";
|
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 {
|
global.zonedCallback = function (callback: Function): Function {
|
||||||
if ((<any>global).zone) {
|
if ((<any>global).zone) {
|
||||||
// Zone v0.5.* style callback wrapping
|
// 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("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 {
|
interface Global {
|
||||||
android?: any;
|
android?: any;
|
||||||
require(id: string): any;
|
require(id: string): any;
|
||||||
|
|
||||||
|
moduleMerge(sourceExports: any, destExports: any): void;
|
||||||
|
|
||||||
registerModule(name: string, loader: ((name: string) => any)): void;
|
registerModule(name: string, loader: ((name: string) => any)): void;
|
||||||
_unregisterModule(name: string): void;
|
|
||||||
/**
|
/**
|
||||||
* Register all modules from a webpack context.
|
* Register all modules from a webpack context.
|
||||||
* The context is one created using the following webpack utility:
|
* 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.),
|
* 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:
|
* 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" }
|
* { ".ts": ".js" }
|
||||||
* ```
|
* ```
|
||||||
* Will resolve lookups for .js to the .ts file.
|
* 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 });
|
registerWebpackModules(context: { keys(): string[], (key: string): any }, extensionMap?: { [originalFileExtension: string]: string });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The NativeScript XML builder, style-scope, application modules use various resources such as:
|
* 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.
|
* 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.
|
* 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[];
|
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;
|
moduleExists(name: string): boolean;
|
||||||
moduleMerge(sourceExports: any, destExports: any): void;
|
|
||||||
getRegisteredModules(): string[];
|
getRegisteredModules(): string[];
|
||||||
|
|
||||||
|
_unregisterModule(name: string): void;
|
||||||
|
|
||||||
|
_isModuleLoadedForUI(moduleName: string): boolean;
|
||||||
|
|
||||||
onGlobalLayoutListener: any;
|
onGlobalLayoutListener: any;
|
||||||
zonedCallback(callback: Function): Function;
|
zonedCallback(callback: Function): Function;
|
||||||
Reflect?: any;
|
Reflect?: any;
|
||||||
Deprecated(target: Object, key?: string | symbol, descriptor?: any): any;
|
Deprecated(target: Object, key?: string | symbol, descriptor?: any): any;
|
||||||
Experimental(target: Object, key?: string | symbol, descriptor?: any): any;
|
Experimental(target: Object, key?: string | symbol, descriptor?: any): any;
|
||||||
|
|
||||||
__native?: any;
|
__native?: any;
|
||||||
__inspector?: any;
|
__inspector?: any;
|
||||||
__extends: any;
|
__extends: any;
|
||||||
|
@ -59,7 +59,7 @@ export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEn
|
|||||||
} else if (entry.moduleName) {
|
} else if (entry.moduleName) {
|
||||||
const moduleName = sanitizeModuleName(entry.moduleName);
|
const moduleName = sanitizeModuleName(entry.moduleName);
|
||||||
const resolvedCodeModuleName = resolveModuleName(moduleName, ""); //`${moduleName}.xml`;
|
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) {
|
if (moduleExports && moduleExports.createPage) {
|
||||||
// Exports has a createPage() method
|
// Exports has a createPage() method
|
||||||
@ -87,7 +87,7 @@ function loadInternal(moduleName: string, moduleExports: any): ComponentModule {
|
|||||||
const resolvedXmlModule = resolveModuleName(moduleName, "xml");
|
const resolvedXmlModule = resolveModuleName(moduleName, "xml");
|
||||||
|
|
||||||
if (resolvedXmlModule) {
|
if (resolvedXmlModule) {
|
||||||
const text = global.loadModule(resolvedXmlModule);
|
const text = global.loadModule(resolvedXmlModule, true);
|
||||||
componentModule = parseInternal(text, moduleExports, resolvedXmlModule, moduleName);
|
componentModule = parseInternal(text, moduleExports, resolvedXmlModule, moduleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ function loadCustomComponent(componentNamespace: string, componentName?: string,
|
|||||||
let subExports = context;
|
let subExports = context;
|
||||||
if (resolvedCodeModuleName) {
|
if (resolvedCodeModuleName) {
|
||||||
// Component has registered code module.
|
// 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.
|
// 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 {
|
try {
|
||||||
if (typeof namespace === "string") {
|
if (typeof namespace === "string") {
|
||||||
resolvedModuleName = resolveModuleName(namespace, "");
|
resolvedModuleName = resolveModuleName(namespace, "");
|
||||||
|
instanceModule = global.loadModule(resolvedModuleName, true);
|
||||||
} else {
|
} else {
|
||||||
// load module from tns-core-modules/ui or mapped paths
|
// load module from tns-core-modules/ui or mapped paths
|
||||||
resolvedModuleName = MODULES[elementName] || UI_PATH +
|
resolvedModuleName = MODULES[elementName] || UI_PATH +
|
||||||
(elementName.toLowerCase().indexOf("layout") !== -1 ? "layouts/" : "") +
|
(elementName.toLowerCase().indexOf("layout") !== -1 ? "layouts/" : "") +
|
||||||
elementName.split(/(?=[A-Z])/).join("-").toLowerCase();
|
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.
|
// Get the component type from module.
|
||||||
const instanceType = instanceModule[elementName] || Object;
|
const instanceType = instanceModule[elementName] || Object;
|
||||||
@ -64,7 +66,7 @@ const getComponentModuleExports = profile("getComponentModuleExports", (instance
|
|||||||
if (codeFileAttribute) {
|
if (codeFileAttribute) {
|
||||||
const resolvedCodeFileModule = resolveModuleName(sanitizeModuleName(codeFileAttribute), "");
|
const resolvedCodeFileModule = resolveModuleName(sanitizeModuleName(codeFileAttribute), "");
|
||||||
if (resolvedCodeFileModule) {
|
if (resolvedCodeFileModule) {
|
||||||
moduleExports = global.loadModule(resolvedCodeFileModule);
|
moduleExports = global.loadModule(resolvedCodeFileModule, true);
|
||||||
(<any>instance).exports = moduleExports;
|
(<any>instance).exports = moduleExports;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Code file with path "${codeFileAttribute}" cannot be found! Looking for webpack module with name "${resolvedCodeFileModule}"`);
|
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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { createViewFromEntry } from "../builder";
|
|||||||
import { profile } from "../../profiling";
|
import { profile } from "../../profiling";
|
||||||
|
|
||||||
import { frameStack, topmost as frameStackTopmost, _pushInFrameStack, _popFromFrameStack, _removeFromFrameStack } from "./frame-stack";
|
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 * from "../core/view";
|
||||||
|
|
||||||
export enum NavigationType {
|
export enum NavigationType {
|
||||||
@ -608,7 +608,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
|
|
||||||
traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync);
|
traceWrite(`Change Handled: Replacing page ${context.path}`, traceCategories.Livesync);
|
||||||
|
|
||||||
this.replacePage(context);
|
this.replacePage(context.path);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -653,9 +653,9 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected replacePage(context: ModuleContext): void {
|
protected replacePage(pagePath: string): void {
|
||||||
const currentBackstackEntry = this._currentEntry;
|
const currentBackstackEntry = this._currentEntry;
|
||||||
const contextModuleName = getModuleName(context.path);
|
const contextModuleName = sanitizeModuleName(pagePath);
|
||||||
|
|
||||||
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
const newPage = <Page>createViewFromEntry({ moduleName: contextModuleName });
|
||||||
const newBackstackEntry: BackstackEntry = {
|
const newBackstackEntry: BackstackEntry = {
|
||||||
|
@ -83,7 +83,7 @@ class CSSSource {
|
|||||||
let appRelativeUri = CSSSource.pathRelativeToApp(uri);
|
let appRelativeUri = CSSSource.pathRelativeToApp(uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cssOrAst = global.loadModule(appRelativeUri);
|
const cssOrAst = global.loadModule(appRelativeUri, true);
|
||||||
if (cssOrAst) {
|
if (cssOrAst) {
|
||||||
if (typeof cssOrAst === "string") {
|
if (typeof cssOrAst === "string") {
|
||||||
// raw-loader
|
// raw-loader
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as types from "./types";
|
import * as types from "./types";
|
||||||
import { dispatchToMainThread, isMainThread } from "./mainthread-helper";
|
import { dispatchToMainThread, isMainThread } from "./mainthread-helper";
|
||||||
|
import { sanitizeModuleName } from "../ui/builder/module-name-sanitizer";
|
||||||
|
|
||||||
export * from "./mainthread-helper";
|
export * from "./mainthread-helper";
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ export function convertString(value: any): any {
|
|||||||
export function getModuleName(path: string): string {
|
export function getModuleName(path: string): string {
|
||||||
let moduleName = path.replace("./", "");
|
let moduleName = path.replace("./", "");
|
||||||
|
|
||||||
return moduleName.substring(0, moduleName.lastIndexOf("."));
|
return sanitizeModuleName(moduleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export module layoutCommon {
|
export module layoutCommon {
|
||||||
|