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

Implement extensibility model for CLI #2724

Merged
merged 1 commit into from
Apr 21, 2017
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
201 changes: 121 additions & 80 deletions PublicAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,17 @@ Public API

This document describes all methods that can be invoked when NativeScript CLI is required as library, i.e.

<table>
<tr>
<td>
JavaScript
</td>
<td>
TypeScript
</td>
</tr>
<tr>
<td>
<pre lang="javascript">
```JavaScript
const tns = require("nativescript");
</pre>
</td>
<td>
<pre lang="typescript">
import * as tns from "nativescript";
</pre>
</td>
</tr>

</table>
```

## Module projectService

`projectService` modules allow you to create new NativeScript application.

* `createProject(projectSettings: IProjectSettings): Promise<void>` - Creates new NativeScript application. By passing `projectSettings` argument you specify the name of the application, the template that will be used, etc.:
### createProject
* Description: `createProject(projectSettings: IProjectSettings): Promise<void>` - Creates new NativeScript application. By passing `projectSettings` argument you specify the name of the application, the template that will be used, etc.:

```TypeScript
/**
* Describes available settings when creating new NativeScript application.
Expand Down Expand Up @@ -73,71 +55,130 @@ interface IProjectSettings {
}
```

Sample usage:
<table>
<tr>
<td>
JavaScript
</td>
<td>
TypeScript
</td>
</tr>
<tr>
<td>
<pre lang="javascript">
* Sample usage:
```JavaScript
const projectSettings = {
projectName: "my-ns-app",
template: "ng",
pathToProject: "/home/my-user/project-dir"
template: "ng",
pathToProject: "/home/my-user/project-dir"
};

tns.projectService.createProject(projectSettings)
.then(() => console.log("Project successfully created."))
.catch((err) => console.log("Unable to create project, reason: ", err);
</pre>
</td>
<td>
<pre lang="typescript">
const projectSettings: IProjectSettings = {
projectName: "my-ns-app",
template: "ng",
pathToProject: "/home/my-user/project-dir"
};
.catch((err) => console.log("Unable to create project, reason: ", err);
```

tns.projectService.createProject(projectSettings)
.then(() => console.log("Project successfully created."))
.catch((err) => console.log("Unable to create project, reason: ", err);
</pre>
</td>
</tr>
</table>

* `isValidNativeScriptProject(projectDir: string): boolean` - Checks if the specified path is a valid NativeScript project. Returns `true` in case the directory is a valid project, `false` otherwise.

Sample usage:
<table>
<tr>
<td>
JavaScript
</td>
<td>
TypeScript
</td>
</tr>
<tr>
<td>
<pre lang="javascript">
const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
</pre>
</td>
<td>
<pre lang="typescript">
### isValidNativeScriptProject
* Definition: `isValidNativeScriptProject(projectDir: string): boolean` - Checks if the specified path is a valid NativeScript project. Returns `true` in case the directory is a valid project, `false` otherwise.

* Sample usage:
```JavaScript
const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
</pre>
</td>
</tr>
</table>
console.log(isValidProject); // true or false
```

## extensibilityService
`extensibilityService` module gives access to methods for working with CLI's extensions - list, install, uninstall, load them. The extensions add new functionality to CLI, so once an extension is loaded, all methods added to it's public API are accessible directly through CLI when it is used as a library. Extensions may also add new commands, so they are accessible through command line when using NativeScript CLI.

A common interface describing the results of a method is `IExtensionData`:
```TypeScript
/**
* Describes each extension.
*/
interface IExtensionData {
/**
* The name of the extension.
*/
extensionName: string;
}
```

### installExtension
Installs specified extension and loads it in the current process, so the functionality that it adds can be used immediately.

* Definition:
```TypeScript
/**
* Installs and loads specified extension.
* @param {string} extensionName Name of the extension to be installed. It may contain version as well, i.e. myPackage, myPackage@1.0.0, myPackage.tgz, https://github.com/myOrganization/myPackage/tarball/master, https://github.com/myOrganization/myPackage etc.
* @returns {Promise<IExtensionData>} Information about installed extensions.
*/
installExtension(extensionName: string): Promise<IExtensionData>;
```

* Usage:
```JavaScript
tns.extensibilityService.installExtension("extension-package")
.then(extensionData => console.log(`Successfully installed extension ${extensionData.extensionName}.`))
.catch(err => console.log("Failed to install extension."));
```

### uninstallExtension
Uninstalls specified extensions, so its functionality will no longer be available through CLI.

* Definition:
```TypeScript
/**
* Uninstalls extension from the installation.
* @param {string} extensionName Name of the extension to be uninstalled.
* @returns {Promise<void>}
*/
uninstallExtension(extensionName: string): Promise<void>;
```

* Usage:
```JavaScript
tns.extensibilityService.uninstallExtension("extension-package")
.then(() => console.log("Successfully uninstalled extension."))
.catch(err => console.log("Failed to uninstall extension."));
```

### getInstalledExtensions
Gets information about all installed extensions.

* Definition:
```TypeScript
/**
* Gets information about installed dependencies - names and versions.
* @returns {IStringDictionary}
*/
getInstalledExtensions(): IStringDictionary;
```

* Usage:
```JavaScript
const installedExtensions = tns.extensibilityService.getInstalledExtensions();
for (let extensionName in installedExtensions) {
const version = installedExtensions[extensionName];
console.log(`The extension ${extensionName} is installed with version ${version}.`);
}
```

### loadExtensions
Loads all currently installed extensions. The method returns array of Promises, one for each installed extension. In case any of the extensions cannot be loaded, only its Promise is rejected.

* Definition
```TypeScript
/**
* Loads all extensions, so their methods and commands can be used from CLI.
* For each of the extensions, a new Promise is returned. It will be rejected in case the extension cannot be loaded. However other promises will not be reflected by this failure.
* In case a promise is rejected, the error will have additional property (extensionName) that shows which is the extension that cannot be loaded in the process.
* @returns {Promise<IExtensionData>[]} Array of promises, each is resolved with information about loaded extension.
*/
loadExtensions(): Promise<IExtensionData>[];
```

* Usage:
```JavaScript
const loadExtensionsPromises = tns.extensibilityService.loadExtensions();
for (let promise of loadExtensionsPromises) {
promise.then(extensionData => console.log(`Loaded extension: ${extensionData.extensionName}.`),
err => {
console.log(`Failed to load extension: ${err.extensionName}`);
console.log(err);
});
}
```

## How to add a new method to Public API
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
Expand Down
35 changes: 35 additions & 0 deletions docs/man_pages/general/extension-install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
extension install
==========

Usage | Synopsis
------|-------
General | `$ tns extension install <Extension>`

Installs specified extension. Each extension adds additional functionality that's accessible directly from NativeScript CLI.

### Attributes

* `<Extension>` is any of the following.
* A `<Name>` or `<Name>@<Version>` where `<Name>` is the name of a package that is published in the npm registry and `<Version>` is a valid version of this plugin.
* A `<Local Path>` to the directory which contains the extension, including its `package.json` file.
* A `<Local Path>` to a `.tar.gz` archive containing a directory with the extension and its `package.json` file.
* A `<URL>` which resolves to a `.tar.gz` archive containing a directory with the extension and its `package.json` file.
* A `<git Remote URL>` which resolves to a `.tar.gz` archive containing a directory with the extension and its `package.json` file.

<% if(isHtml) { %>
### Related Commands

Command | Description
----------|----------
[extension](extension.html) | Prints information about all installed extensions.
[extension-uninstall](extension-uninstall.html) | Uninstalls specified extension.
[autocomplete-status](autocomplete-status.html) | Prints the current status of your command-line completion settings.
[autocomplete-enable](autocomplete-enable.html) | Configures your current command-line completion settings.
[autocomplete-disable](autocomplete-disable.html) | Disables command-line completion for bash and zsh shells.
[usage-reporting](usage-reporting.html) | Configures anonymous usage reporting for the NativeScript CLI.
[error-reporting](error-reporting.html) | Configures anonymous error reporting for the NativeScript CLI.
[doctor](doctor.html) | Checks your system for configuration problems which might prevent the NativeScript CLI from working properly.
[proxy](proxy.html) | Displays proxy settings.
[proxy clear](proxy-clear.html) | Clears proxy settings.
[proxy set](proxy-set.html) | Sets proxy settings.
<% } %>
31 changes: 31 additions & 0 deletions docs/man_pages/general/extension-uninstall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
extension uninstall
==========

Usage | Synopsis
------|-------
General | `$ tns extension uninstall <Extension>`

Uninstalls specified extension. After that you will not be able to use the functionality that this extensions adds to NativeScript CLI.

### Attributes

* `<Extension>` is the name of the extension as listed in its `package.json` file.

<% if(isHtml) { %>
### Related Commands

Command | Description
----------|----------
[extension](extension.html) | Prints information about all installed extensions.
[extension-uninstall](extension-uninstall.html) | Uninstalls specified extension.
[extension-install](extension-install.html) | Installs specified extension.
[autocomplete-status](autocomplete-status.html) | Prints the current status of your command-line completion settings.
[autocomplete-enable](autocomplete-enable.html) | Configures your current command-line completion settings.
[autocomplete-disable](autocomplete-disable.html) | Disables command-line completion for bash and zsh shells.
[usage-reporting](usage-reporting.html) | Configures anonymous usage reporting for the NativeScript CLI.
[error-reporting](error-reporting.html) | Configures anonymous error reporting for the NativeScript CLI.
[doctor](doctor.html) | Checks your system for configuration problems which might prevent the NativeScript CLI from working properly.
[proxy](proxy.html) | Displays proxy settings.
[proxy clear](proxy-clear.html) | Clears proxy settings.
[proxy set](proxy-set.html) | Sets proxy settings.
<% } %>
25 changes: 25 additions & 0 deletions docs/man_pages/general/extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
extension
==========

Usage | Synopsis
------|-------
General | `$ tns extension`

Prints information about all installed extensions.

<% if(isHtml) { %>
### Related Commands

Command | Description
----------|----------
[extension-install](extension-install.html) | Installs specified extension.
[autocomplete-status](autocomplete-status.html) | Prints the current status of your command-line completion settings.
[autocomplete-enable](autocomplete-enable.html) | Configures your current command-line completion settings.
[autocomplete-disable](autocomplete-disable.html) | Disables command-line completion for bash and zsh shells.
[usage-reporting](usage-reporting.html) | Configures anonymous usage reporting for the NativeScript CLI.
[error-reporting](error-reporting.html) | Configures anonymous error reporting for the NativeScript CLI.
[doctor](doctor.html) | Checks your system for configuration problems which might prevent the NativeScript CLI from working properly.
[proxy](proxy.html) | Displays proxy settings.
[proxy clear](proxy-clear.html) | Clears proxy settings.
[proxy set](proxy-set.html) | Sets proxy settings.
<% } %>
7 changes: 7 additions & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,10 @@ $injector.require("projectChangesService", "./services/project-changes-service")
$injector.require("emulatorPlatformService", "./services/emulator-platform-service");

$injector.require("staticConfig", "./config");

$injector.require("requireService", "./services/require-service");

$injector.requireCommand("extension|*list", "./commands/extensibility/list-extensions");
$injector.requireCommand("extension|install", "./commands/extensibility/install-extension");
$injector.requireCommand("extension|uninstall", "./commands/extensibility/uninstall-extension");
$injector.requirePublic("extensibilityService", "./services/extensibility-service");
13 changes: 13 additions & 0 deletions lib/commands/extensibility/install-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class InstallExtensionCommand implements ICommand {
constructor(private $extensibilityService: IExtensibilityService,
private $stringParameterBuilder: IStringParameterBuilder,
private $logger: ILogger) { }

public async execute(args: string[]): Promise<void> {
const extensionData = await this.$extensibilityService.installExtension(args[0]);
this.$logger.info(`Successfully installed extension ${extensionData.extensionName}.`);
}

allowedParameters: ICommandParameter[] = [this.$stringParameterBuilder.createMandatoryParameter("You have to provide a valid name for extension that you want to install.")];
}
$injector.registerCommand("extension|install", InstallExtensionCommand);
24 changes: 24 additions & 0 deletions lib/commands/extensibility/list-extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as helpers from "../../common/helpers";

export class ListExtensionsCommand implements ICommand {
constructor(private $extensibilityService: IExtensibilityService,
private $logger: ILogger) { }

public async execute(args: string[]): Promise<void> {
const installedExtensions = this.$extensibilityService.getInstalledExtensions();
if (_.keys(installedExtensions).length) {
this.$logger.info("Installed extensions:");
const data = _.map(installedExtensions, (version, name) => {
return [name, version];
});

const table = helpers.createTable(["Name", "Version"], data);
this.$logger.out(table.toString());
} else {
this.$logger.info("No extensions installed.");
}
}

allowedParameters: ICommandParameter[] = [];
}
$injector.registerCommand("extension|*list", ListExtensionsCommand);
14 changes: 14 additions & 0 deletions lib/commands/extensibility/uninstall-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export class UninstallExtensionCommand implements ICommand {
constructor(private $extensibilityService: IExtensibilityService,
private $stringParameterBuilder: IStringParameterBuilder,
private $logger: ILogger) { }

public async execute(args: string[]): Promise<void> {
const extensionName = args[0];
await this.$extensibilityService.uninstallExtension(extensionName);
this.$logger.info(`Successfully uninstalled extension ${extensionName}`);
}

allowedParameters: ICommandParameter[] = [this.$stringParameterBuilder.createMandatoryParameter("You have to provide a valid name for extension that you want to uninstall.")];
}
$injector.registerCommand("extension|uninstall", UninstallExtensionCommand);
Loading