FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(HMR): apply changes in application styles at runtime · NativeScript/NativeScript@42a1491 · GitHub

Commit 42a1491

Browse files
committed
feat(HMR): apply changes in application styles at runtime
Expose `HmrContext` interface. Apply changes in `app.css` instantly. Avoid navigation on livesync when changes in `app.css` have been made. Apply changes in `app.css` on back navigation.
1 parent 6095779 commit 42a1491

9 files changed

Lines changed: 110 additions & 34 deletions

File tree

‎tns-core-modules/application/application-common.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ export function setApplication(instance: iOSApplication | AndroidApplication): v
7070
app = instance;
7171
}
7272

73-
export function livesync() {
73+
export function livesync(context?: HmrContext) {
7474
events.notify(<EventData>{ eventName: "livesync", object: app });
7575
const liveSyncCore = global.__onLiveSyncCore;
7676
if (liveSyncCore) {
77-
liveSyncCore();
77+
liveSyncCore(context);
7878
}
7979
}
8080

@@ -92,7 +92,7 @@ export function loadAppCss(): void {
9292
events.notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: app, cssFile: getCssFileName() });
9393
} catch (e) {
9494
throw new Error(`The file ${getCssFileName()} couldn't be loaded! ` +
95-
`You may need to register it inside ./app/vendor.ts.`);
95+
`You may need to register it inside ./app/vendor.ts.`);
9696
}
9797
}
9898

‎tns-core-modules/application/application.android.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,12 @@ export function getNativeApplication(): android.app.Application {
212212
return nativeApp;
213213
}
214214

215-
global.__onLiveSync = function () {
215+
global.__onLiveSync = function __onLiveSync(context?: HmrContext) {
216216
if (androidApp && androidApp.paused) {
217217
return;
218218
}
219219

220-
livesync();
220+
livesync(context);
221221
};
222222

223223
function initLifecycleCallbacks() {

‎tns-core-modules/application/application.ios.ts‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from "./application-common";
1818
import { createViewFromEntry } from "../ui/builder";
1919
import { ios as iosView, View } from "../ui/core/view";
2020
import { Frame, NavigationEntry } from "../ui/frame";
21+
import { loadCss } from "../ui/styling/style-scope";
2122
import * as utils from "../utils/utils";
2223
import { profile, level as profilingLevel, Level } from "../profiling";
2324

@@ -225,10 +226,21 @@ class IOSApplication implements IOSApplicationDefinition {
225226
}
226227
}
227228

228-
public _onLivesync(): void {
229-
// If view can't handle livesync set window controller.
230-
if (!this._rootView._onLivesync()) {
231-
this.setWindowContent();
229+
public _onLivesync(context?: HmrContext): void {
230+
let executeLivesync = true;
231+
// HMR has context, livesync does not
232+
if (context) {
233+
if (context.module === getCssFileName()) {
234+
loadCss(context.module);
235+
this._rootView._onCssStateChange();
236+
executeLivesync = false;
237+
}
238+
}
239+
if (executeLivesync) {
240+
// If view can't handle livesync set window controller.
241+
if (!this._rootView._onLivesync()) {
242+
this.setWindowContent();
243+
}
232244
}
233245
}
234246

@@ -264,8 +276,8 @@ exports.ios = iosApp;
264276
setApplication(iosApp);
265277

266278
// attach on global, so it can be overwritten in NativeScript Angular
267-
(<any>global).__onLiveSyncCore = function () {
268-
iosApp._onLivesync();
279+
(<any>global).__onLiveSyncCore = function __onLiveSyncCore(context?: HmrContext) {
280+
iosApp._onLivesync(context);
269281
}
270282

271283
let mainEntry: NavigationEntry;
@@ -373,10 +385,10 @@ function setViewControllerView(view: View): void {
373385
}
374386
}
375387

376-
global.__onLiveSync = function () {
388+
global.__onLiveSync = function __onLiveSync(context?: HmrContext) {
377389
if (!started) {
378390
return;
379391
}
380392

381-
livesync();
393+
livesync(context);
382394
}

‎tns-core-modules/module.d.ts‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ declare namespace NodeJS {
5151
__native?: any;
5252
__inspector?: any;
5353
__extends: any;
54-
__onLiveSync: () => void;
55-
__onLiveSyncCore: () => void;
54+
__onLiveSync: (context?: { type: string, module: string }) => void;
55+
__onLiveSyncCore: (context?: { type: string, module: string }) => void;
5656
__onUncaughtError: (error: NativeScriptError) => void;
5757
TNS_WEBPACK?: boolean;
5858
__requireOverride?: (name: string, dir: string) => any;
@@ -64,6 +64,27 @@ declare function clearTimeout(timeoutId: number): void;
6464
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number;
6565
declare function clearInterval(intervalId: number): void;
6666

67+
declare enum HmrType {
68+
markup = "markup",
69+
script = "script",
70+
style = "style"
71+
}
72+
73+
/**
74+
* Define a context for Hot Module Replacement.
75+
*/
76+
interface HmrContext {
77+
/**
78+
* The type of module for replacement.
79+
*/
80+
type: HmrType;
81+
82+
/**
83+
* The module for replacement.
84+
*/
85+
module: string;
86+
}
87+
6788
/**
6889
* An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code.
6990
*/

‎tns-core-modules/ui/frame/frame.android.ts‎

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
_updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType
1818
} from "./fragment.transitions";
1919

20+
import { loadCss } from "../styling/style-scope";
2021
import { profile } from "../../profiling";
2122

2223
// TODO: Remove this and get it from global to decouple builder for angular
@@ -82,13 +83,24 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
8283
return attachStateChangeListener;
8384
}
8485

85-
export function reloadPage(): void {
86+
export function reloadPage(context?: HmrContext): void {
8687
const activity = application.android.foregroundActivity;
8788
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
8889
const rootView: View = callbacks.getRootView();
8990

90-
if (!rootView || !rootView._onLivesync()) {
91-
callbacks.resetActivityContent(activity);
91+
let executeLivesync = true;
92+
// HMR has context, livesync does not
93+
if (context) {
94+
if (context.module === application.getCssFileName()) {
95+
loadCss(context.module);
96+
rootView._onCssStateChange();
97+
executeLivesync = false;
98+
}
99+
}
100+
if (executeLivesync) {
101+
if (!rootView || !rootView._onLivesync()) {
102+
callbacks.resetActivityContent(activity);
103+
}
92104
}
93105
}
94106

@@ -469,19 +481,19 @@ export class Frame extends FrameBase {
469481
switch (this.actionBarVisibility) {
470482
case "never":
471483
return false;
472-
484+
473485
case "always":
474486
return true;
475-
487+
476488
default:
477489
if (page.actionBarHidden !== undefined) {
478490
return !page.actionBarHidden;
479491
}
480-
492+
481493
if (this._android && this._android.showActionBar !== undefined) {
482494
return this._android.showActionBar;
483495
}
484-
496+
485497
return true;
486498
}
487499
}
@@ -846,14 +858,14 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
846858
// parent while its supposed parent believes it properly removed its children; in order to "force" the child to
847859
// lose its parent we temporarily add it to the parent, and then remove it (addViewInLayout doesn't trigger layout pass)
848860
const nativeView = page.nativeViewProtected;
849-
if (nativeView != null) {
850-
const parentView = nativeView.getParent();
861+
if (nativeView != null) {
862+
const parentView = nativeView.getParent();
851863
if (parentView instanceof android.view.ViewGroup) {
852864
if (parentView.getChildCount() === 0) {
853865
parentView.addViewInLayout(nativeView, -1, new org.nativescript.widgets.CommonLayoutParams());
854866
}
855867

856-
parentView.removeView(nativeView);
868+
parentView.removeView(nativeView);
857869
}
858870
}
859871

‎tns-core-modules/ui/page/page-common.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ export class PageBase extends ContentView implements PageDefinition {
1717
public static navigatedToEvent = "navigatedTo";
1818
public static navigatingFromEvent = "navigatingFrom";
1919
public static navigatedFromEvent = "navigatedFrom";
20-
20+
2121
private _navigationContext: any;
2222
private _actionBar: ActionBar;
2323

2424
public _frame: Frame;
25-
25+
2626
public actionBarHidden: boolean;
2727
public enableSwipeBackNavigation: boolean;
2828
public backgroundSpanUnderStatusBar: boolean;
2929
public hasActionBar: boolean;
30-
30+
3131
get navigationContext(): any {
3232
return this._navigationContext;
3333
}
@@ -89,7 +89,7 @@ export class PageBase extends ContentView implements PageDefinition {
8989
const frame = this.parent;
9090
return frame instanceof Frame ? frame : undefined;
9191
}
92-
92+
9393
private createNavigatedData(eventName: string, isBackNavigation: boolean): NavigatedData {
9494
return {
9595
eventName: eventName,
@@ -103,6 +103,10 @@ export class PageBase extends ContentView implements PageDefinition {
103103
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
104104
this._navigationContext = context;
105105

106+
if (!this._cssState.isSelectorsLatestVersionApplied()) {
107+
this._onCssStateChange();
108+
}
109+
106110
//https://github.com/NativeScript/NativeScript/issues/731
107111
if (!isBackNavigation && bindingContext !== undefined && bindingContext !== null) {
108112
this.bindingContext = bindingContext;

‎tns-core-modules/ui/styling/style-scope.d.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export class CssState {
1919
* Gets the static selectors that match the view and the dynamic selectors that may potentially match the view.
2020
*/
2121
public changeMap: ChangeMap<ViewBase>;
22+
23+
/**
24+
* Checks whether style scope and CSS state selectors are in sync.
25+
*/
26+
public isSelectorsLatestVersionApplied(): boolean
2227
}
2328

2429
export class StyleScope {
@@ -29,6 +34,9 @@ export class StyleScope {
2934
public static createSelectorsFromImports(tree: SyntaxTree, keyframes: Object): RuleSet[];
3035
public ensureSelectors(): number;
3136

37+
public isApplicationCssSelectorsLatestVersionApplied(): boolean;
38+
public isLocalCssSelectorsLatestVersionApplied(): boolean;
39+
3240
public applySelectors(view: ViewBase): void
3341
public query(options: Node): SelectorCore[];
3442

‎tns-core-modules/ui/styling/style-scope.ts‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export function removeTaggedAdditionalCSS(tag: String | Number): Boolean {
271271
changed = true;
272272
}
273273
}
274-
if (changed) { mergeCssSelectors(); }
274+
if (changed) { mergeCssSelectors(); }
275275
return changed;
276276
}
277277

@@ -307,7 +307,7 @@ function onLiveSync(args: applicationCommon.CssChangedEventData): void {
307307
loadCss(applicationCommon.getCssFileName());
308308
}
309309

310-
const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
310+
export const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
311311
if (!cssFile) {
312312
return undefined;
313313
}
@@ -343,6 +343,7 @@ export class CssState {
343343
_appliedChangeMap: Readonly<ChangeMap<ViewBase>>;
344344
_appliedPropertyValues: Readonly<{}>;
345345
_appliedAnimations: ReadonlyArray<kam.KeyframeAnimation>;
346+
_appliedSelectorsVersion: number;
346347

347348
_match: SelectorsMatch<ViewBase>;
348349
_matchInvalid: boolean;
@@ -367,6 +368,15 @@ export class CssState {
367368
}
368369
}
369370

371+
public isSelectorsLatestVersionApplied(): boolean {
372+
if (this._appliedSelectorsVersion && this.view._styleScope) {
373+
this.view._styleScope.ensureSelectors();
374+
return this.view._styleScope._getSelectorsVersion() === this._appliedSelectorsVersion;
375+
} else {
376+
return true;
377+
}
378+
}
379+
370380
public onLoaded(): void {
371381
if (this._matchInvalid) {
372382
this.updateMatch();
@@ -381,6 +391,7 @@ export class CssState {
381391

382392
@profile
383393
private updateMatch() {
394+
this._appliedSelectorsVersion = this.view._styleScope._getSelectorsVersion();
384395
this._match = this.view._styleScope ? this.view._styleScope.matchSelectors(this.view) : CssState.emptyMatch;
385396
this._matchInvalid = false;
386397
}
@@ -597,8 +608,8 @@ export class StyleScope {
597608
}
598609

599610
public ensureSelectors(): number {
600-
if (this._applicationCssSelectorsAppliedVersion !== applicationCssSelectorVersion ||
601-
this._localCssSelectorVersion !== this._localCssSelectorsAppliedVersion ||
611+
if (!this.isApplicationCssSelectorsLatestVersionApplied() ||
612+
!this.isLocalCssSelectorsLatestVersionApplied() ||
602613
!this._mergedCssSelectors) {
603614

604615
this._createSelectors();
@@ -607,6 +618,14 @@ export class StyleScope {
607618
return this._getSelectorsVersion();
608619
}
609620

621+
public isApplicationCssSelectorsLatestVersionApplied(): boolean {
622+
return this._applicationCssSelectorsAppliedVersion === applicationCssSelectorVersion;
623+
}
624+
625+
public isLocalCssSelectorsLatestVersionApplied(): boolean {
626+
return this._localCssSelectorsAppliedVersion === this._localCssSelectorVersion;
627+
}
628+
610629
@profile
611630
private _createSelectors() {
612631
let toMerge: RuleSet[][] = [];

‎tsconfig.shared.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626
"tns-core-modules/*": ["tns-core-modules/*"]
2727
}
2828
}
29-
}
29+
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL