lolelo
lolelo

Reputation: 740

index.d.ts when creating a NativeScript plugin?

I have recently started to make a plugin in NativeScript - following the documention. And I made some successfull runs with npm run demo.ios in the src folder.

However when starting to build the actual plugin I intended on - I am encountering the following error:

file:///node_modules/tns-core-modules/ui/builder/builder.js:179:0 JS ERROR Error: Building UI from XML. @app-root.xml:1:1 undefined is not an object (evaluating 'b.prototype')

I have set up an extension method onto ViewBase in my myplugin.common.ts file that I attempt to call from the demo application

The problem could lie in the index.d.ts I believe. According the docs the myplugin.ios.d.ts and myplugin.android.d.ts files should be copied in there or defined manually. I am attempting to copy the myplugin.common.d.ts file content in there instead. Is that valid?

I have also noticed two runs seem to be necceseray - since the first run generates the ...d.ts. Of which content then has to copied into index.d.ts.


some code...

myplugin.common.ts

declare module "tns-core-modules/ui/core/view-base/view-base" {
  interface ViewBase {
    myExenstionMethod(myNumber: number): void;
  }
}
ViewBase.prototype.myExenstionMethod = function(myNumber: number) {
  console.log("myNumber: " + myNumber);
}

and index.d.ts (copied from myplugin.common.d.ts):

declare module "tns-core-modules/ui/core/view-base/view-base" {
    interface ViewBase {
        myExenstionMethod(myNumber: number): void;
    }
}

It is shown in the demo project:

enter image description here

And the error comes from simply importing the extension method from the plugin in my demo project:

import "nativescript-myplugin";

Note

I created a new plugin project - only adding the extension method and it worked. So there was something else in the original plugin project that caused the error. Adding declare module in index.d.ts was not needed.


Hitting it again

with the same error as before - when instantiating myplugin.ios.ts/myplugin.android.ts - myplugin class - from inside myplugin.common.ts. I initially thought about creating a platform specific singleton instance to interact with in .common.ts - but that does not seem to work?

import { Gestures as iOSGestures} from "./gestures.ios";
import {Gestures as AndroidGestures} from "./gestures.android";

export abstract class Common extends Observable {
  private static _instance = null;

  static instance(os: string): Common {
    if(Common._instance == null) {
      /* crash from instantiating iOSGestures or AndroidGestures */
      this._instance = os == "iOS" ? new iOSGestures() : new AndroidGestures();
    }
    return Common._instance;
  }
  abstract _attach(viewBase: ViewBase, gesture: string): void;
}

What is strange is that that particular code is never called from anywhere yet. And commenting out that line I can create that error by simply having dead code containing the instantiation:

// still inside .common.ts
import { Gestures } from "./gestures.android";

const object = {
  methodInFile()  {
    const g = new Gestures() // <--
  }
}

Here is the android.ts:

export class Gestures extends Common {
    _attach(viewBase: ViewBase, gesture: string): void {
        console.log("_attach android");
        if(gesture == touch.down) {

        }
    }
}

It could be some sort of conflict with the index.d.ts?:

import { Common } from './gestures.common';
export declare class Gestures extends Common {
  // define your typings manually
  // or..
  // take the ios or android .d.ts files and copy/paste them here
}

This my first time attempting to build a plugin NativeScript - so I might be setting things completely wrong. I hope to provide my plugin as an extension method on ViewBase though - and the extension method defined in common.ts is called successfully.


// inside common.ts
export class Gestures extends Common {
  _attach(): void {
      console.log("_attach ios");
  }
}
const object = {
  methodInFile()  {
    const g = new Gestures() // <--
  }
}

... works.

But when the class is imported from another file, having new Gestures() - causes the crash as mentioned. It neither works creating a new file - ios.common.ts. So it could be that npm run demo.ios is generating faulty js files on build? I also removed the content of index.d.ts completely - so it can't involved causing the error.


Making the common.ts an interface - that gets implemented in .ios.ts and android.ts. I encountered a new error;

file:///app/home/home-page.ts:17:27 JS ERROR TypeError: page.addGestureHandler is not a function. (In 'page.addGestureHandler("hello")', 'page.addGestureHandler' is undefined)

My conclusion so far is that I can't have Common as an abstract class or interface - and instantiate ios.ts and android.ts as singletons. The class extending abstract Common - or implementing interface Common - must be in the same file as (common.ts). This can be related to the fact I am using an extension method as entry point into the plugin.

Upvotes: 1

Views: 618

Answers (2)

lolelo
lolelo

Reputation: 740

I finally manage to set up the plugin in a correct and satisfying manner

The two main point are (if using extension method as entry point):

declare module "tns-core-modules/ui/core/view-base" {
    interface ViewBase {
        addGestureHandler(gesture: string): void;
    }
}
ViewBase.prototype.addGestureHandler = function(gesture: string) {
    console.log("addGestureHandler: " + gesture);
    // then calling platform specific singleton
}

... has to defined in inside a separate file from myplugin.common.ts. I call it myplugin.ts.

Also with this set up. myplugin.ios.ts and myplugin.android.ts has to removed. Create new files: ios.impl.ts and android.impl.ts. The common.ts then ends up containing the abstract class or interface.


So in the demo application home-page.ts the usage of the plugin becomes like so:

import "nativescript-gestures"; // available only by having a common.ts file src folder in the plugin

import { NavigatedData, Page } from "tns-core-modules/ui/page";
import { HomeViewModel } from "./home-view-model";

export function onNavigatingTo(args: NavigatedData) {
    const page = <Page>args.object;
    page.bindingContext = new HomeViewModel();
    page.addGestureHandler("hello"); // <-- addGestureHandler extension method
}

Upvotes: 0

Manoj
Manoj

Reputation: 21908

TypeScript declarations (*.d.ts) are just for the sake of IntelliSense support with your IDE. Hence there is nothing called valid or invalid as long you export right definitions you want to expose. Most plugins will just export index.d.ts that will usually expose everything from *-common.ts.

Update:

In order to prevent generating .d.ts and .js.map files, you will set declaration & sourceMap flags in src/tsconfig.json to false.

You can not avoid .js files, that's the only sole reason of using TypeScript compiler (Converting TS to JS). While publishing your plugin or using it in demo app, .js files are the ones actually used.

Between, without sourceMap debugging may be difficult for you.

Upvotes: 1

Related Questions