Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypeScript declaration files #1578

Merged
merged 2 commits into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ npm-debug.log
.project
test/memory.json
.nyc_output

dist
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add dist to .npmignore too.


1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ docs/html
npm-debug.log
.travis.yml
.nyc_output
dist

36 changes: 36 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

// Type definitions for loopback-datasource-juggler 3.x
// Project: https://github.com/strongloop/loopback-datasource-juggler
// Definitions by: Raymond Feng <https://github.com/raymondfeng>
// TypeScript Version: 2.8

/**
* Experimental TypeScript definitions to capture types of the key artifacts
* from `loopback-datasource-juggler` module. One of the main purposes is to
* leverage such types in `LoopBack 4`'s bridge to juggler.
*
* Please note some of the classes, properties, methods, and functions are
* intentionally not included in the definitions because of one of the following
* factors:
*
* - They are internal
* - They are to be deprecated
*/
export * from './types/common';
export * from './types/model';
export * from './types/relation';
export * from './types/query';
export * from './types/datasource';
export * from './types/kv-model';
export * from './types/persisted-model';
export * from './types/scope';
export * from './types/transaction-mixin';
export * from './types/relation-mixin';
export * from './types/observer-mixin';
export * from './types/validation-mixin';
export * from './types/inclusion-mixin';
export * from './types/connector';
4 changes: 3 additions & 1 deletion lib/datasource.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function DataSource(name, settings, modelBuilder) {
}.bind(this));

// define DataAccessObject.prototype methods
Object.keys(dao.prototype).forEach(function(name) {
Object.keys(dao.prototype || []).forEach(function(name) {
var fn = dao.prototype[name];
this.DataAccessObject.prototype[name] = fn;
if (typeof fn === 'function') {
Expand Down Expand Up @@ -2541,6 +2541,7 @@ DataSource.prototype.ready = function(obj, args) {
* @param {Function} [cb] Callback function
*/
DataSource.prototype.ping = function(cb) {
cb = cb || utils.createPromiseCallback();
var self = this;
if (self.connector.ping) {
this.connector.ping(cb);
Expand All @@ -2552,6 +2553,7 @@ DataSource.prototype.ping = function(cb) {
cb(err);
});
}
return cb.promise;
};

/*! The hidden property call is too expensive so it is not used that much
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@
"url": "https://github.com/strongloop/loopback-datasource-juggler"
},
"main": "index.js",
"types": "index.d.ts",
"browser": {
"depd": "./lib/browser.depd.js"
},
"scripts": {
"coverage": "nyc report --reporter=text-lcov | coveralls",
"lint": "eslint .",
"tsc": "tsc -p tsconfig.json --outDir dist",
"test": "nyc mocha",
"posttest": "npm run lint"
"posttest": "npm run tsc && npm run lint"
},
"devDependencies": {
"async-iterators": "^0.2.2",
Expand All @@ -40,9 +42,11 @@
"loopback-connector-throwing": "file:./test/fixtures/loopback-connector-throwing",
"mocha": "^3.2.0",
"nyc": "^11.1.0",
"should": "^8.4.0"
"should": "^8.4.0",
"typescript": "^2.8.3"
},
"dependencies": {
"@types/node": "^10.0.3",
"async": "~2.1.4",
"bluebird": "^3.1.1",
"debug": "^3.1.0",
Expand Down
18 changes: 18 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"compilerOptions": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused why do we need a tsconfig file, when this project does not contain any TypeScript sources to compile, only .d.ts files. Could you please clarify?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The presence of tsconfig.json file allows VsCode to validate the syntax of .d.ts files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

I think we should have a build step to run that validations, otherwise our .d.ts files can quickly become invalid. I consider this as a must have for this pull request.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see you have added npm run tsc, we are good here 👍

"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"strictNullChecks": true,

"lib": ["es2018", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"target": "es2017",
"sourceMap": true,
"declaration": true
},
"include": ["types/", "index.d.ts"],
"exclude": ["node_modules/**"]
}
28 changes: 28 additions & 0 deletions types/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/**
* Objects with open properties
*/
export interface AnyObject<T = any> {
[property: string]: T;
}

/**
* Type alias for options object
*/
export type Options = AnyObject<any>;

/**
* Type alias for Node.js callback functions
*/
export type Callback<T = any> = (err: any, result?: T) => void;

/**
* Return export type for promisified Node.js async methods.
*
* Note that juggler uses Bluebird, not the native Promise.
*/
export type PromiseOrVoid<T = any> = PromiseLike<T> | void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any import or definition of PromiseLike<T>, perhaps an oversight on your side?

I think we need to introduce some kind of a smoke test (as part of this pull request!) to at least verify that type definitions can be parsed & loaded by tsc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PromiseLike is a built-in type from lib.es5.d.ts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added npm run tsc to validate *.d.ts files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

46 changes: 46 additions & 0 deletions types/connector.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {Callback, DataSource, Options, PromiseOrVoid} from '..';

// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/**
* Connector from `loopback-connector` module
*/
export interface Connector {
name: string; // Name/type of the connector
dataSource?: DataSource;
connect(callback?: Callback): PromiseOrVoid; // Connect to the underlying system
disconnect(callback?: Callback): PromiseOrVoid; // Disconnect from the underlying system
ping(callback?: Callback): PromiseOrVoid; // Ping the underlying system
execute?(...args: any[]): Promise<any>;
}

/**
* Base connector class
*/
export declare class ConnectorBase implements Connector {
name: string; // Name/type of the connector;
dataSource?: DataSource;
connect(callback?: Callback): PromiseOrVoid; // Connect to the underlying system
disconnect(callback?: Callback): PromiseOrVoid; // Disconnect from the underlying system
ping(callback?: Callback): PromiseOrVoid; // Ping the underlying system
execute?(...args: any[]): Promise<any>;

/**
* Initialize the connector against the given data source
*
* @param {DataSource} dataSource The dataSource
* @param {Function} [callback] The callback function
*/
static initialize(dataSource: DataSource, callback?: Callback): void;

constructor(settings?: Options);
}

export declare class Memory extends ConnectorBase {}

export declare class KeyValueMemoryConnector extends ConnectorBase {}

export declare class Transient extends ConnectorBase {}
177 changes: 177 additions & 0 deletions types/datasource.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {AnyObject, Callback, Options, PromiseOrVoid} from './common';
import {Connector} from './connector';
import {
ModelBaseClass,
ModelBuilder,
ModelDefinition,
PropertyDefinition,
} from './model';

/**
* LoopBack models can manipulate data via the DataSource object.
* Attaching a `DataSource` to a `Model` adds instance methods and static methods to the `Model`.
*
* Define a data source to persist model data.
* To create a DataSource programmatically, call `createDataSource()` on the LoopBack object; for example:
* ```js
* var oracle = loopback.createDataSource({
* connector: 'oracle',
* host: '111.22.333.44',
* database: 'MYDB',
* username: 'username',
* password: 'password'
* });
* ```
*
* All classes in single dataSource share same the connector type and
* one database connection.
*
* For example, the following creates a DataSource, and waits for a connection callback.
*
* ```
* var dataSource = new DataSource('mysql', { database: 'myapp_test' });
* dataSource.define(...);
* dataSource.on('connected', function () {
* // work with database
* });
* ```
* @class DataSource
* @param {String} [name] Optional name for datasource.
* @options {Object} settings Database-specific settings to establish connection (settings depend on specific connector).
* The table below lists a typical set for a relational database.
* @property {String} connector Database connector to use. For any supported connector, can be any of:
*
* - The connector module from `require(connectorName)`.
* - The full name of the connector module, such as 'loopback-connector-oracle'.
* - The short name of the connector module, such as 'oracle'.
* - A local module under `./connectors/` folder.
* @property {String} host Database server host name.
* @property {String} port Database server port number.
* @property {String} username Database user name.
* @property {String} password Database password.
* @property {String} database Name of the database to use.
* @property {Boolean} debug Display debugging information. Default is false.
*
* The constructor allows the following styles:
*
* 1. new DataSource(dataSourceName, settings). For example:
* - new DataSource('myDataSource', {connector: 'memory'});
* - new DataSource('myDataSource', {name: 'myDataSource', connector: 'memory'});
* - new DataSource('myDataSource', {name: 'anotherDataSource', connector: 'memory'});
*
* 2. new DataSource(settings). For example:
* - new DataSource({name: 'myDataSource', connector: 'memory'});
* - new DataSource({connector: 'memory'});
*
* 3. new DataSource(connectorModule, settings). For example:
* - new DataSource(connectorModule, {name: 'myDataSource})
* - new DataSource(connectorModule)
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a good idea to duplicate the jsdoc comments in typing files? I am concerned that the content in .d.ts files will quickly get out of sync with the original comments in .js files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the package is used by other modules such as @loopback/repository, VSCode uses the tsdocs from .d.ts file, IIUC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that makes sense.

It would be great to find a way how to automatically sync these jsdoc/tsdoc comments, but that's out of scope of this pull request.

export declare class DataSource {
name: string;
settings: Options;

connected?: boolean;
connecting?: boolean;

connector?: Connector;

DataAccessObject: AnyObject & {prototype: AnyObject};

constructor(name?: string, settings?: Options, modelBuilder?: ModelBuilder);

constructor(
connectorModule: Connector,
settings?: Options,
modelBuilder?: ModelBuilder,
);

/**
* Create a model class
* @param name Name of the model
* @param properties An object of property definitions
* @param options Options for model settings
*/
createModel<T extends ModelBaseClass>(
name: string,
properties?: AnyObject,
options?: Options,
): T;

getModel(modelName: string): ModelBaseClass | undefined;

/**
* Attach an existing model to a data source.
* This will mixin all of the data access object functions (DAO) into your
* modelClass definition.
* @param {ModelBaseClass} modelClass The model constructor that will be enhanced
* by DataAccessObject mixins.
*/
attach(modelClass: ModelBaseClass): ModelBaseClass;

automigrate(models: string | string[], callback?: Callback): PromiseOrVoid;

autoupdate(models: string | string[], callback?: Callback): PromiseOrVoid;

discoverModelDefinitions(
options?: Options,
callback?: Callback<ModelDefinition[]>,
): PromiseOrVoid<ModelDefinition[]>;

discoverModelProperties(
modelName: string,
options?: Options,
callback?: Callback<PropertyDefinition[]>,
): PromiseOrVoid<PropertyDefinition[]>;

discoverPrimaryKeys(
modelName: string,
options?: Options,
callback?: Callback<PropertyDefinition[]>,
): PromiseOrVoid<PropertyDefinition[]>;

discoverForeignKeys(
modelName: string,
options?: Options,
callback?: Callback<PropertyDefinition[]>,
): PromiseOrVoid<PropertyDefinition[]>;

discoverExportedForeignKeys(
modelName: string,
options?: Options,
callback?: Callback<PropertyDefinition[]>,
): PromiseOrVoid<PropertyDefinition[]>;

discoverAndBuildModels(
modelName: string,
options?: Options,
callback?: Callback<{[name: string]: ModelBaseClass}>,
): PromiseOrVoid<{[name: string]: ModelBaseClass}>;

discoverSchema(
tableName: string,
options?: Options,
callback?: Callback<AnyObject>,
): PromiseOrVoid<AnyObject>;

discoverSchemas(
tableName: string,
options?: Options,
callback?: Callback<AnyObject[]>,
): PromiseOrVoid<AnyObject[]>;

buildModelFromInstance(
modelName: string,
jsonObject: AnyObject,
options?: Options,
): ModelBaseClass;

connect(callback?: Callback): PromiseOrVoid;
disconnect(callback?: Callback): PromiseOrVoid;
ping(callback?: Callback): PromiseOrVoid;
}
Loading