Skip to content

Commit 9dd6f20

Browse files
authored
Merge pull request #3836 from NativeScript/lini/plugin-create-with-template
feat: add template option to plugin create command
2 parents d74399e + f391833 commit 9dd6f20

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

docs/man_pages/lib-management/plugin-create.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ position: 1
66

77
Usage | Synopsis
88
---|---
9-
Create a new plugin | `$ tns plugin create <Plugin Repository Name> [--path <Directory>]`
9+
Create from the default plugin seed | `$ tns plugin create <Plugin Repository Name> [--path <Directory>]`
10+
Create from a custom plugin seed | `$ tns plugin create <Plugin Repository Name> [--path <Directory>] --template <Template>`
1011

1112
Creates a new project for NativeScript plugin development. The project uses the [NativeScript Plugin Seed](https://github.com/NativeScript/nativescript-plugin-seed) as a base and contains the following directories:
1213

@@ -20,9 +21,19 @@ The project is setup for easy commit in Github, which is why the command will as
2021
### Options
2122

2223
* `--path` - Specifies the directory where you want to create the project, if different from the current directory.
24+
* `--template` - Specifies the custom seed archive, which you want to use to create your plugin. If `--template` is not set, the NativeScript CLI creates the plugin from the default NativeScript Plugin Seed.
2325
* `--username` - Specifies the Github username, which will be used to build the URLs in the plugin's package.json file.
2426
* `--pluginName` - Used to set the default file and class names in the plugin source.
2527

2628
### Attributes
2729

28-
* `<Plugin Repository Name>` is the name of repository where your plugin will reside. A directory with the same name will be created. For example: `nativescript-awesome-list`. If a directory with the name already exists and is not empty, the plugin create command will fail.
30+
* `<Plugin Repository Name>` is the name of repository where your plugin will reside. A directory with the same name will be created. For example: `nativescript-awesome-list`. If a directory with the name already exists and is not empty, the plugin create command will fail.<% if(isHtml) { %>
31+
* `<Template>` can be a URL or a local path to a `.tar.gz` file with the contents of a seed repository. This must be a clone of the [NativeScript Plugin Seed](https://github.com/NativeScript/nativescript-plugin-seed) and must contain a `src` directory with a package.json file and a script at `src/scripts/postclone.js`. After the archive is extracted, the postclone script will be executed with the username (`gitHubUsername`) and plugin name (`pluginName`) parameters given to the `tns plugin create` command prompts. For more information, visit the default plugin seed repository and [examine the source script](https://github.com/NativeScript/nativescript-plugin-seed/blob/master/src/scripts/postclone.js) there. Examples:
32+
33+
* Using a local file:
34+
35+
`tns plugin create nativescript-testplugin --template ../seeds/seed1.tar.gz`
36+
37+
* Using a `.tar.gz` file from a tag called `v4.0` in a Github repository:
38+
39+
`tns plugin create nativescript-testplugin --template https://github.com/NativeScript/nativescript-plugin-seed/archive/v.4.0.tar.gz`<% } %>

lib/commands/plugin/create-plugin.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ export class CreatePluginCommand implements ICommand {
1818
public async execute(args: string[]): Promise<void> {
1919
const pluginRepoName = args[0];
2020
const pathToProject = this.$options.path;
21+
const selectedTemplate = this.$options.template;
2122
const selectedPath = path.resolve(pathToProject || ".");
2223
const projectDir = path.join(selectedPath, pluginRepoName);
2324

24-
this.$logger.printMarkdown("Downloading the latest version of NativeScript Plugin Seed...");
25-
await this.downloadPackage(projectDir);
26-
27-
this.$logger.printMarkdown("Executing initial plugin configuration script...");
25+
await this.downloadPackage(selectedTemplate, projectDir);
2826
await this.setupSeed(projectDir, pluginRepoName);
2927

3028
this.$logger.printMarkdown("Solution for `%s` was successfully created.", pluginRepoName);
@@ -39,6 +37,8 @@ export class CreatePluginCommand implements ICommand {
3937
}
4038

4139
private async setupSeed(projectDir: string, pluginRepoName: string): Promise<void> {
40+
this.$logger.printMarkdown("Executing initial plugin configuration script...");
41+
4242
const config = this.$options;
4343
const spinner = this.$terminalSpinnerService.createSpinner();
4444
const cwd = path.join(projectDir, "src");
@@ -66,15 +66,21 @@ export class CreatePluginCommand implements ICommand {
6666
}
6767
}
6868

69-
private async downloadPackage(projectDir: string): Promise<void> {
69+
private async downloadPackage(selectedTemplate: string, projectDir: string): Promise<void> {
7070
this.$fs.createDirectory(projectDir);
7171

7272
if (this.$fs.exists(projectDir) && !this.$fs.isEmptyDir(projectDir)) {
7373
this.$errors.fail("Path already exists and is not empty %s", projectDir);
7474
}
7575

76+
if (selectedTemplate) {
77+
this.$logger.printMarkdown("Make sure your custom template is compatible with the Plugin Seed at https://github.com/NativeScript/nativescript-plugin-seed/");
78+
} else {
79+
this.$logger.printMarkdown("Downloading the latest version of NativeScript Plugin Seed...");
80+
}
81+
7682
const spinner = this.$terminalSpinnerService.createSpinner();
77-
const packageToInstall = "https://github.com/NativeScript/nativescript-plugin-seed/archive/master.tar.gz";
83+
const packageToInstall = selectedTemplate || "https://github.com/NativeScript/nativescript-plugin-seed/archive/master.tar.gz";
7884
try {
7985
spinner.start();
8086
await this.$pacoteService.extractPackage(packageToInstall, projectDir);

test/plugin-create.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ import { CreatePluginCommand } from "../lib/commands/plugin/create-plugin";
44
import { assert } from "chai";
55
import helpers = require("../lib/common/helpers");
66

7+
interface IPacoteOutput {
8+
packageName: string;
9+
destinationDirectory: string;
10+
}
11+
712
const originalIsInteractive = helpers.isInteractive;
813
const dummyArgs = ["dummyProjectName"];
914
const dummyUser = "devUsername";
1015
const dummyName = "devPlugin";
16+
const dummyPacote: IPacoteOutput = { packageName: "", destinationDirectory: "" };
1117

1218
function createTestInjector() {
1319
const testInjector = new Yok();
@@ -21,7 +27,9 @@ function createTestInjector() {
2127
testInjector.register("npm", stubs.NpmInstallationManagerStub);
2228
testInjector.register("options", {
2329
username: undefined,
24-
pluginName: undefined
30+
pluginName: undefined,
31+
template: undefined,
32+
path: undefined
2533
});
2634

2735
testInjector.register("terminalSpinnerService", {
@@ -34,7 +42,11 @@ function createTestInjector() {
3442

3543
testInjector.register("pacoteService", {
3644
manifest: () => Promise.resolve(),
37-
extractPackage: () => Promise.resolve()
45+
extractPackage: (packageName: string, destinationDirectory: string) => {
46+
dummyPacote.destinationDirectory = destinationDirectory;
47+
dummyPacote.packageName = packageName;
48+
return Promise.resolve();
49+
}
3850
});
3951

4052
testInjector.register("createCommand", CreatePluginCommand);
@@ -63,6 +75,24 @@ describe("Plugin create command tests", () => {
6375
assert.isRejected(createPluginCommand.canExecute([]));
6476
});
6577

78+
it("should use correct directory when path parameter is passed", async () => {
79+
helpers.isInteractive = () => false;
80+
const dummyPath = "dummyPath";
81+
options.path = dummyPath;
82+
dummyPacote.destinationDirectory = "";
83+
await createPluginCommand.execute(dummyArgs);
84+
assert.include(dummyPacote.destinationDirectory, dummyPath);
85+
});
86+
87+
it("should use correct download path when template parameter is passed", async () => {
88+
helpers.isInteractive = () => false;
89+
const dummyTemplate = "dummyTemplate";
90+
options.template = dummyTemplate;
91+
dummyPacote.packageName = "";
92+
await createPluginCommand.execute(dummyArgs);
93+
assert.equal(dummyPacote.packageName, dummyTemplate);
94+
});
95+
6696
it("should pass when only project name is set in non-interactive shell.", async () => {
6797
helpers.isInteractive = () => false;
6898
await createPluginCommand.execute(dummyArgs);

0 commit comments

Comments
 (0)