Add CONTRIBUTING.md and update guides (#4511)

* Removing legacy files

* Dev-setup

* CodeConventions revised

* CONTRIBUTING.md first draft

* Update CreateNewModule.md

* Module exapmles

* Writing unit tests article

* Minor Changes

* Organize PR steps
This commit is contained in:
Alexander Vakrilov
2017-07-06 16:17:53 +03:00
committed by GitHub
parent 974ef9d932
commit e1bba78784
14 changed files with 455 additions and 3320 deletions

View File

@@ -3,60 +3,62 @@
1. Each module resides in a separate folder.
2. There must be a package.json file in this folder which tells the NS Runtime which is the main file of the module to load.
3. There is a declaration (*.d.ts) file describing the public API of the module.
4. When there is a ***.android.ts** named file this tells our CLI that this file is Android-specific and should be icluded in Android builds ONLY. When a build is started for the Android platform, the **.android** part of the file is stripped in the application package. For example **foo.android.js** will become ***foo.js**. Same convention works for ***.ios.ts** files.
4. When there is a ***.android.ts** named file this tells our CLI that this file is Android-specific and should be included in Android builds ONLY. When a build is started for the Android platform, the **.android** part of the file is stripped in the application package. For example **foo.android.js** will become ***foo.js**. Same convention works for ***.ios.ts** files.
## Declaration and implementation files
There are several major scenarios when writing modules:
### The module implementation contains pure JavaScript code ONLY and does not depend on native APIs. In this case the entire logic is executed on the JS Virtual Machine side and the TNS Runtime is not involved.
_Example:_ [matrix module](tns-core-modules/matrix)
**Declaration file (foo.d.ts):**
```javascript
declare module "foo"{
function a();
class Foo {
public var1: number;
}
```typescript
export function a();
export class Foo {
public var1: number;
}
```
**Implementation file (foo.ts):**
```javascript
import * as definition from "foo";
```typescript
import { Foo as FooDefinition } from ".";
export function a(){
// do somethign here
export function a() {
// do something here
}
// require the definition and put implements clause to ensure API consistency between the declaration and implementation
export class Foo implements definition.Foo {
export class Foo implements FooDefinition {
public var1: number;
}
```
### The module implementation depends on native APIs ONLY and the common pure JavaScript code between platform-specific implementations is minimal.
_Example:_ [timer module](tns-core-modules/timer)
**Declaration file (foo.d.ts):**
```javascript
declare module "foo"{
class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```typescript
export class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```
**Android implementation file (foo.android.ts):**
```javascript
import * as definition from "foo";
```typescript
import { Foo as FooDefinition } from ".";
// require the definition and put implements clause to ensure API consistency between the declaration and implementation
export class Foo implements definition.Foo {
export class Foo implements FooDefinition {
public running: number;
public start(): void {
// Call android APIs - e.g. android.os.SystemClock.[xxx]
@@ -71,11 +73,11 @@ export class Foo implements definition.Foo {
**iOS implementation file (foo.ios.ts):**
```javascript
import * as definition from "foo";
```typescript
import { Foo as FooDefinition } from ".";
// require the definition and put implements clause to ensure API consistency between the declaration and implementation
export class Foo implements definition.Foo {
export class Foo implements FooDefinition {
public running: number;
public start(): void {
// Call iOS APIs - e.g. Foundation.NSObject.[xxx]
@@ -92,27 +94,29 @@ export class Foo implements definition.Foo {
In this case we will need to reuse the common JavaScript code and to split the implementation only for the platform specific native APIs. There are two different approaches here:
* _Separate the common implementation (code) in a base class. Add two specific files that inherit the base class and provide the platform-specific implementation:_
#### Separate the common implementation (code) in a base class. Add two specific files that inherit the base class and provide the platform-specific implementation
This is the way most of the UI modules are written.
_Example:_ [image module](tns-core-modules/ui/image)
**Declaration file (foo.d.ts):**
```javascript
declare module "foo"{
class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```typescript
export class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```
**Common implementation file (foo-common.ts):**
```javascript
import * as definition from "foo";
```typescript
import { FooBase as FooDefinition } from ".";
// require the definition and put implements clause to ensure API consistency between the declaration and implementation
export class Foo implements definition.Foo {
export class FooBase implements FooDefinition {
public running: number;
public start(): void {
this.running = true;
@@ -127,11 +131,11 @@ export class Foo implements definition.Foo {
**Android implementation file (foo.android.ts):**
```javascript
import * as common from "foo-common";
```typescript
import { FooBase } from "./foo-common";
// require the common file and extend the base common implementation
export class Foo extends common.Foo {
export class Foo extends FooBase {
public start(): void {
// call the base method which does the common job
super.start();
@@ -147,11 +151,11 @@ export class Foo extends common.Foo {
**iOS implementation file (foo.ios.ts):**
```javascript
import * as common from "foo-common";
```typescript
import { FooBase } from "./foo-common";
// require the common file and extend the base common implementation
export class Foo extends common.Foo {
export class Foo extends FooBase {
public start(): void {
// call the base method which does the common job
super.start();
@@ -165,34 +169,33 @@ export class Foo extends common.Foo {
}
```
* _Extract the platform specific implementation in a separate Facade and aggregate/use it within the JavaScript implementation:_
#### Extract the platform specific implementation in a separate Facade and aggregate/use it within the JavaScript implementation
_Example:_ [file-system module](tns-core-modules/file-system) (_Note:_ `file-system-access` is the platform specific implementation)
**Declaration file (foo.d.ts):**
```javascript
declare module "foo"{
class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```typescript
export class Foo {
public running: number;
public start(): void;
public stop(): void;
}
```
**Native Implementation Declaration file (foo-native.d.ts):**
```javascript
```typescript
//@private
// The above statement marks this definition as private so that it is not visible to the users
declare module "foo-native"{
function startNative();
function stopNative();
}
export function startNative();
export function stopNative();
```
**Android Native Implementation file (foo-native.android.ts):**
```javascript
```typescript
export function startNative(){
// call native code here
}
@@ -203,7 +206,7 @@ export function stopNative(){
**iOS Native Implementation file (foo-native.ios.ts):**
```javascript
```typescript
export function startNative(){
// call native code here
}
@@ -214,22 +217,22 @@ export function stopNative(){
**Common implementation file (foo.ts):**
```javascript
import * as definition from "foo";
import * as fooNative from "foo-native";
```typescript
import { FooBase as FooDefinition } from ".";
import { startNative, stopNative } from "./foo-native";
// require the definition and put implements clause to ensure API consistency between the declaration and implementation
export class Foo implements definition.Foo {
export class Foo implements FooDefinition {
public running: number;
public start(): void {
this.running = true;
// do the native call through the Facade
fooNative.startNative();
startNative();
}
public stop(): void {
this.running = false;
// do the native call through the Facade
fooNative.stopNative();
stopNative();
}
}
```