Skip to content
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
29 changes: 27 additions & 2 deletions PublicAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ interface IExtensionData {
```

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

* Definition:
```TypeScript
/**
* Installs and loads specified extension.
* Installs a 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.
*/
Expand Down Expand Up @@ -180,6 +180,31 @@ for (let promise of loadExtensionsPromises) {
}
```

### loadExtension
Loads a specified extension.

* Definition
```TypeScript
/**
* Loads a single extension, so its methods and commands can be used from CLI.
* @param {string} extensionName Name of the extension to be installed. It may contain version as well, i.e. myPackage, myPackage@1.0.0
* A Promise is returned. It will be rejected in case the extension cannot be loaded.
* @returns {Promise<IExtensionData>} Promise, resolved with IExtensionData.
*/
loadExtension(extensionName: string): Promise<IExtensionData>;
```

* Usage:
```JavaScript
tns.extensibilityService.loadExtension("my-extension")
.then(extensionData => console.log(`Loaded extension: ${extensionData.extensionName}.`),
err => {
console.log(`Failed to load extension: ${err.extensionName}`);
console.log(err);
});
}
```

## settingsService
`settingsService` module provides a way to configure various settings.

Expand Down
3 changes: 3 additions & 0 deletions lib/commands/extensibility/install-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export class InstallExtensionCommand implements ICommand {
public async execute(args: string[]): Promise<void> {
const extensionData = await this.$extensibilityService.installExtension(args[0]);
this.$logger.info(`Successfully installed extension ${extensionData.extensionName}.`);

await this.$extensibilityService.loadExtension(extensionData.extensionName);
this.$logger.info(`Successfully loaded extension ${extensionData.extensionName}.`);
}

allowedParameters: ICommandParameter[] = [this.$stringParameterBuilder.createMandatoryParameter("You have to provide a valid name for extension that you want to install.")];
Expand Down
10 changes: 9 additions & 1 deletion lib/definitions/extensibility.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface IExtensionData {
*/
interface IExtensibilityService {
/**
* Installs and loads specified extension.
* Installs a 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.
Expand All @@ -35,6 +35,14 @@ interface IExtensibilityService {
*/
loadExtensions(): Promise<IExtensionData>[];

/**
* Loads a single extension, so its methods and commands can be used from CLI.
* @param {string} extensionName Name of the extension to be installed. It may contain version as well, i.e. myPackage, myPackage@1.0.0
* A Promise is returned. It will be rejected in case the extension cannot be loaded.
* @returns {Promise<IExtensionData>} Promise, resolved with IExtensionData.
*/
loadExtension(extensionName: string): Promise<IExtensionData>;

/**
* Gets information about installed dependencies - names and versions.
* @returns {IStringDictionary}
Expand Down
5 changes: 3 additions & 2 deletions lib/services/extensibility-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class ExtensibilityService implements IExtensibilityService {
const installResultInfo = await this.$npm.install(packageName, this.pathToExtensions, npmOpts);
this.$logger.trace(`Finished installation of extension '${extensionName}'. Trying to load it now.`);

return await this.loadExtension(installResultInfo.name);
return { extensionName: installResultInfo.name };
}

@exported("extensibilityService")
Expand Down Expand Up @@ -74,7 +74,8 @@ export class ExtensibilityService implements IExtensibilityService {
return null;
}

private async loadExtension(extensionName: string): Promise<IExtensionData> {
@exported("extensibilityService")
public async loadExtension(extensionName: string): Promise<IExtensionData> {
try {
await this.assertExtensionIsInstalled(extensionName);

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"glob": "^7.0.3",
"iconv-lite": "0.4.11",
"inquirer": "0.9.0",
"ios-device-lib": "0.4.2",
"ios-device-lib": "0.4.3",
"ios-mobileprovision-finder": "1.0.9",
"ios-sim-portable": "~3.0.0",
"lockfile": "1.0.1",
Expand Down
2 changes: 1 addition & 1 deletion test/nativescript-cli-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("nativescript-cli-lib", () => {
localBuildService: ["build"],
deviceLogProvider: null,
npm: ["install", "uninstall", "view", "search"],
extensibilityService: ["loadExtensions", "getInstalledExtensions", "installExtension", "uninstallExtension"],
extensibilityService: ["loadExtensions", "loadExtension", "getInstalledExtensions", "installExtension", "uninstallExtension"],
analyticsService: ["startEqatecMonitor"],
debugService: ["debug"]
};
Expand Down
64 changes: 33 additions & 31 deletions test/services/extensibility-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,37 +136,6 @@ describe("extensibilityService", () => {
const actualResult = await extensibilityService.installExtension(extensionName);
assert.deepEqual(actualResult, { extensionName });
});

it("throws error that has extensionName property when unable to load extension", async () => {
const expectedErrorMessage = "Require failed";

const extensionName = "extension1";
const testInjector = getTestInjector();
const fs: IFileSystem = testInjector.resolve("fs");
fs.exists = (pathToCheck: string): boolean => path.basename(pathToCheck) !== extensionName;

fs.readDirectory = (dir: string): string[] => [extensionName];

const npm: INodePackageManager = testInjector.resolve("npm");
npm.install = async (packageName: string, pathToSave: string, config?: any): Promise<any> => ({ name: extensionName });

const requireService: IRequireService = testInjector.resolve("requireService");
requireService.require = (pathToRequire: string) => {
throw new Error(expectedErrorMessage);
};

const extensibilityService: IExtensibilityService = testInjector.resolve(ExtensibilityService);
let isErrorRaised = false;
try {
await extensibilityService.installExtension(extensionName);
} catch (err) {
isErrorRaised = true;
assert.deepEqual(err.message, `Unable to load extension ${extensionName}. You will not be able to use the functionality that it adds.`);
assert.deepEqual(err.extensionName, extensionName);
}

assert.isTrue(isErrorRaised);
});
});

describe("loadExtensions", () => {
Expand Down Expand Up @@ -609,4 +578,37 @@ describe("extensibilityService", () => {
assert.deepEqual(extensibilityService.getInstalledExtensions(), dependencies);
});
});

describe("loadExtension", () => {
it("throws error that has extensionName property when unable to load extension", async () => {
const expectedErrorMessage = "Require failed";

const extensionName = "extension1";
const testInjector = getTestInjector();
const fs: IFileSystem = testInjector.resolve("fs");
fs.exists = (pathToCheck: string): boolean => path.basename(pathToCheck) !== extensionName;

fs.readDirectory = (dir: string): string[] => [extensionName];

const npm: INodePackageManager = testInjector.resolve("npm");
npm.install = async (packageName: string, pathToSave: string, config?: any): Promise<any> => ({ name: extensionName });

const requireService: IRequireService = testInjector.resolve("requireService");
requireService.require = (pathToRequire: string) => {
throw new Error(expectedErrorMessage);
};

const extensibilityService: IExtensibilityService = testInjector.resolve(ExtensibilityService);
let isErrorRaised = false;
try {
await extensibilityService.loadExtension(extensionName);
} catch (err) {
isErrorRaised = true;
assert.deepEqual(err.message, `Unable to load extension ${extensionName}. You will not be able to use the functionality that it adds.`);
assert.deepEqual(err.extensionName, extensionName);
}

assert.isTrue(isErrorRaised);
});
});
});