diff --git a/e2e/ui-tests-app/app/main-page.ts b/e2e/ui-tests-app/app/main-page.ts
index 8055d26e5..f669befef 100644
--- a/e2e/ui-tests-app/app/main-page.ts
+++ b/e2e/ui-tests-app/app/main-page.ts
@@ -39,6 +39,7 @@ export function pageLoaded(args: EventData) {
examples.set("progress-bar", "progress-bar/main-page");
examples.set("date-picker", "date-picker/date-picker-page");
examples.set("nested-frames", "nested-frames/main-page");
+ examples.set("screen-qualifiers", "screen-qualifiers/main-page");
page.bindingContext = new MainPageViewModel(wrapLayout, examples);
const parent = page.getViewById("parentLayout");
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.ios.xml b/e2e/ui-tests-app/app/screen-qualifiers/main-page.ios.xml
new file mode 100644
index 000000000..ca44532cd
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.ios.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.land.xml b/e2e/ui-tests-app/app/screen-qualifiers/main-page.land.xml
new file mode 100644
index 000000000..201396f44
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.land.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH120.port.xml b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH120.port.xml
new file mode 100644
index 000000000..13ffb3e6a
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH120.port.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.ts b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.ts
new file mode 100644
index 000000000..7548770c3
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.ts
@@ -0,0 +1,10 @@
+import { EventData } from "tns-core-modules/data/observable";
+import { Observable } from "tns-core-modules/data/observable";
+import { Page } from "tns-core-modules/ui/page";
+
+export function pageLoaded(args: EventData) {
+ const page = args.object;
+
+ page.bindingContext = new Observable();
+ page.bindingContext.set("currentDate", "No date for alternate screens!");
+}
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.xml b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.xml
new file mode 100644
index 000000000..d2f00eef1
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.ts b/e2e/ui-tests-app/app/screen-qualifiers/main-page.ts
new file mode 100644
index 000000000..20ccea1f2
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.ts
@@ -0,0 +1,10 @@
+import { EventData } from "tns-core-modules/data/observable";
+import { Observable } from "tns-core-modules/data/observable";
+import { Page } from "tns-core-modules/ui/page";
+
+export function pageLoaded(args: EventData) {
+ const page = args.object;
+
+ page.bindingContext = new Observable();
+ page.bindingContext.set("currentDate", new Date());
+}
\ No newline at end of file
diff --git a/e2e/ui-tests-app/app/screen-qualifiers/main-page.xml b/e2e/ui-tests-app/app/screen-qualifiers/main-page.xml
new file mode 100644
index 000000000..83ab2fb29
--- /dev/null
+++ b/e2e/ui-tests-app/app/screen-qualifiers/main-page.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nativescript-core/module-name-resolver/module-name-resolver.ts b/nativescript-core/module-name-resolver/module-name-resolver.ts
index 03dd50f2d..6ce9e7cc7 100644
--- a/nativescript-core/module-name-resolver/module-name-resolver.ts
+++ b/nativescript-core/module-name-resolver/module-name-resolver.ts
@@ -1,7 +1,7 @@
import { ModuleNameResolver as ModuleNameResolverDefinition, ModuleListProvider } from "./";
import { screen, device } from "../platform/platform";
import * as appCommonModule from "../application/application-common";
-import { PlatformContext, findMatch } from "./qualifier-matcher";
+import { PlatformContext, findMatch, stripQualifiers } from "./qualifier-matcher";
import { registerModulesFromFileSystem } from "./non-bundle-workflow-compat";
import {
isEnabled as traceEnabled,
@@ -44,6 +44,9 @@ export class ModuleNameResolver implements ModuleNameResolverDefinition {
registerModulesFromFileSystem(path);
}
+ // This call will return a clean path without qualifiers
+ path = stripQualifiers(path);
+
let candidates = this.getCandidates(path, ext);
result = findMatch(path, ext, candidates, this.context);
diff --git a/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.d.ts b/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.d.ts
index 46ff19d9f..1279a7928 100644
--- a/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.d.ts
+++ b/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.d.ts
@@ -11,3 +11,5 @@ export interface PlatformContext {
}
export function findMatch(path: string, ext: string, candidates: Array, context: PlatformContext): string;
+
+export function stripQualifiers(path: string): string
diff --git a/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.ts b/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.ts
index 15056b71e..8b4f7487f 100644
--- a/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.ts
+++ b/nativescript-core/module-name-resolver/qualifier-matcher/qualifier-matcher.ts
@@ -5,17 +5,20 @@ const MIN_H: string = "minH";
const PRIORITY_STEP = 10000;
interface QualifierSpec {
- isMatch(value: string): boolean;
+ isMatch(path: string): boolean;
+ getMatchOccurences(path: string): Array;
getMatchValue(value: string, context: PlatformContext): number;
}
const minWidthHeightQualifier: QualifierSpec = {
- isMatch: function (value: string): boolean {
- return value.indexOf(MIN_WH) === 0;
-
+ isMatch: function (path: string): boolean {
+ return (new RegExp(`.${MIN_WH}\\d+`).test(path));
+ },
+ getMatchOccurences: function (path: string): Array {
+ return path.match(new RegExp(`.${MIN_WH}\\d+`));
},
getMatchValue(value: string, context: PlatformContext): number {
- const numVal = parseInt(value.substr(MIN_WH.length));
+ const numVal = parseInt(value.substr(MIN_WH.length + 1));
if (isNaN(numVal)) {
return -1;
}
@@ -30,12 +33,14 @@ const minWidthHeightQualifier: QualifierSpec = {
};
const minWidthQualifier: QualifierSpec = {
- isMatch: function (value: string): boolean {
- return value.indexOf(MIN_W) === 0 && value.indexOf(MIN_WH) < 0;
-
+ isMatch: function (path: string): boolean {
+ return (new RegExp(`.${MIN_W}\\d+`).test(path)) && !(new RegExp(`.${MIN_WH}\\d+`).test(path));
+ },
+ getMatchOccurences: function (path: string): Array {
+ return path.match(new RegExp(`.${MIN_W}\\d+`));
},
getMatchValue(value: string, context: PlatformContext): number {
- const numVal = parseInt(value.substr(MIN_W.length));
+ const numVal = parseInt(value.substr(MIN_W.length + 1));
if (isNaN(numVal)) {
return -1;
}
@@ -50,12 +55,14 @@ const minWidthQualifier: QualifierSpec = {
};
const minHeightQualifier: QualifierSpec = {
- isMatch: function (value: string): boolean {
- return value.indexOf(MIN_H) === 0 && value.indexOf(MIN_WH) < 0;
-
+ isMatch: function (path: string): boolean {
+ return (new RegExp(`.${MIN_H}\\d+`).test(path)) && !(new RegExp(`.${MIN_WH}\\d+`).test(path));
+ },
+ getMatchOccurences: function (path: string): Array {
+ return path.match(new RegExp(`.${MIN_H}\\d+`));
},
getMatchValue(value: string, context: PlatformContext): number {
- const numVal = parseInt(value.substr(MIN_H.length));
+ const numVal = parseInt(value.substr(MIN_H.length + 1));
if (isNaN(numVal)) {
return -1;
}
@@ -70,26 +77,31 @@ const minHeightQualifier: QualifierSpec = {
};
const platformQualifier: QualifierSpec = {
- isMatch: function (value: string): boolean {
- return value === "android" ||
- value === "ios";
-
+ isMatch: function (path: string): boolean {
+ return path.includes(".android") || path.includes(".ios");
+ },
+ getMatchOccurences: function (path: string): Array {
+ return [".android", ".ios"];
},
getMatchValue(value: string, context: PlatformContext): number {
- return value === context.os.toLowerCase() ? 1 : -1;
+ const val = value.substr(1);
+
+ return val === context.os.toLowerCase() ? 1 : -1;
}
};
const orientationQualifier: QualifierSpec = {
- isMatch: function (value: string): boolean {
- return value === "land" ||
- value === "port";
-
+ isMatch: function (path: string): boolean {
+ return path.includes(".land") || path.includes(".port");
+ },
+ getMatchOccurences: function (path: string): Array {
+ return [".land", ".port"];
},
getMatchValue(value: string, context: PlatformContext): number {
+ const val = value.substr(1);
const isLandscape: number = (context.width > context.height) ? 1 : -1;
- return (value === "land") ? isLandscape : -isLandscape;
+ return (val === "land") ? isLandscape : -isLandscape;
}
};
@@ -102,51 +114,63 @@ const supportedQualifiers: Array = [
platformQualifier
];
-function checkQualifiers(qualifiers: Array, context: PlatformContext): number {
+function checkQualifiers(path: string, context: PlatformContext): number {
let result = 0;
- let value: number;
- for (let i = 0; i < qualifiers.length; i++) {
- if (qualifiers[i]) {
- value = checkQualifier(qualifiers[i], context);
- if (value < 0) {
+ for (let i = 0; i < supportedQualifiers.length; i++) {
+ let qualifier = supportedQualifiers[i];
+ if (qualifier.isMatch(path))
+ {
+ let occurences = qualifier.getMatchOccurences(path);
+ // Always get the last qualifier among identical occurences
+ result = qualifier.getMatchValue(occurences[occurences.length - 1], context);
+ if (result < 0)
+ {
// Non of the supported qualifiers matched this or the match was not satisfied
return -1;
}
- result += value;
+ result += (supportedQualifiers.length - i) * PRIORITY_STEP;
+
+ return result;
}
}
return result;
}
-function checkQualifier(value: string, context: PlatformContext) {
- let result: number;
+export function stripQualifiers(path: string): string {
+ // Strip qualifiers from path if any
for (let i = 0; i < supportedQualifiers.length; i++) {
- if (supportedQualifiers[i].isMatch(value)) {
- result = supportedQualifiers[i].getMatchValue(value, context);
- if (result > 0) {
- result += (supportedQualifiers.length - i) * PRIORITY_STEP;
+ let qualifier = supportedQualifiers[i];
+ if (qualifier.isMatch(path))
+ {
+ let occurences = qualifier.getMatchOccurences(path);
+ for (let j = 0; j < occurences.length; j++)
+ {
+ path = path.replace(occurences[j], "");
}
-
- return result;
}
}
- return -1;
+ return path;
}
export function findMatch(path: string, ext: string, candidates: Array, context: PlatformContext): string {
+ let fullPath: string = ext ? (path + ext) : path;
let bestValue = -1;
let result: string = null;
for (let i = 0; i < candidates.length; i++) {
const filePath = candidates[i];
- const qualifiersStr: string = filePath.substr(path.length, filePath.length - path.length - (ext ? ext.length : 0));
- const qualifiers = qualifiersStr.split(".");
+ // Check if candidate is correct for given path
+ const cleanFilePath: string = stripQualifiers(filePath);
+ if (cleanFilePath !== fullPath)
+ {
+ continue;
+ }
- const value = checkQualifiers(qualifiers, context);
+ const value = checkQualifiers(filePath, context);
if (value >= 0 && value > bestValue) {
bestValue = value;