From 4f64bace62627a9c8e2a59a38bd53bbe1250a2ac Mon Sep 17 00:00:00 2001 From: DimitrisRK <55595100+DimitrisRK@users.noreply.github.com> Date: Thu, 18 Jun 2020 06:12:11 +0300 Subject: [PATCH] fix(module-name-resolver): livesync page qualifier handling (#8637) closes https://github.com/NativeScript/NativeScript/issues/8622 Co-authored-by: Dimitris - Rafail Katsampas --- e2e/ui-tests-app/app/main-page.ts | 1 + .../app/screen-qualifiers/main-page.ios.xml | 10 ++ .../app/screen-qualifiers/main-page.land.xml | 10 ++ .../main-page.minWH120.port.xml | 10 ++ .../screen-qualifiers/main-page.minWH360.ts | 10 ++ .../screen-qualifiers/main-page.minWH360.xml | 10 ++ .../app/screen-qualifiers/main-page.ts | 10 ++ .../app/screen-qualifiers/main-page.xml | 10 ++ .../module-name-resolver.ts | 5 +- .../qualifier-matcher/qualifier-matcher.d.ts | 2 + .../qualifier-matcher/qualifier-matcher.ts | 108 +++++++++++------- 11 files changed, 143 insertions(+), 43 deletions(-) create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.ios.xml create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.land.xml create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH120.port.xml create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.ts create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.minWH360.xml create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.ts create mode 100644 e2e/ui-tests-app/app/screen-qualifiers/main-page.xml diff --git a/e2e/ui-tests-app/app/main-page.ts b/e2e/ui-tests-app/app/main-page.ts index 8055d26e57..f669befef8 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 0000000000..ca44532cd4 --- /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 0000000000..201396f44a --- /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 0000000000..13ffb3e6a0 --- /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 0000000000..7548770c3a --- /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 0000000000..d2f00eef16 --- /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 0000000000..20ccea1f21 --- /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 0000000000..83ab2fb29f --- /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 03dd50f2df..6ce9e7cc70 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 46ff19d9f1..1279a79283 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 15056b71e6..8b4f7487ff 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;