-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathcreate-plugin.ts
150 lines (126 loc) · 6.2 KB
/
create-plugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import * as path from "path";
import { isInteractive } from "../../common/helpers";
export class CreatePluginCommand implements ICommand {
public allowedParameters: ICommandParameter[] = [];
public userMessage = "What is your GitHub username?\n(will be used to update the Github URLs in the plugin's package.json)";
public nameMessage = "What will be the name of your plugin?\n(use lowercase characters and dashes only)";
public includeTypeScriptDemoMessage = 'Do you want to include a "TypeScript NativeScript" application linked with your plugin to make development easier?';
public includeAngularDemoMessage = 'Do you want to include an "Angular NativeScript" application linked with your plugin to make development easier?';
public pathAlreadyExistsMessageTemplate = "Path already exists and is not empty %s";
constructor(private $options: IOptions,
private $errors: IErrors,
private $terminalSpinnerService: ITerminalSpinnerService,
private $logger: ILogger,
private $pacoteService: IPacoteService,
private $fs: IFileSystem,
private $childProcess: IChildProcess,
private $prompter: IPrompter,
private $packageManager: INodePackageManager) { }
public async execute(args: string[]): Promise<void> {
const pluginRepoName = args[0];
const pathToProject = this.$options.path;
const selectedTemplate = this.$options.template;
const selectedPath = path.resolve(pathToProject || ".");
const projectDir = path.join(selectedPath, pluginRepoName);
// Must be out of try catch block, because will throw error if folder alredy exists and we don't want to delete it.
this.ensurePackageDir(projectDir);
try {
await this.downloadPackage(selectedTemplate, projectDir);
await this.setupSeed(projectDir, pluginRepoName);
} catch (err) {
// The call to this.ensurePackageDir() above will throw error if folder alredy exists, so it is safe to delete here.
this.$fs.deleteDirectory(projectDir);
throw err;
}
this.$logger.printMarkdown("Solution for `%s` was successfully created.", pluginRepoName);
}
public async canExecute(args: string[]): Promise<boolean> {
if (!args[0]) {
this.$errors.fail("You must specify the plugin repository name.");
}
return true;
}
private async setupSeed(projectDir: string, pluginRepoName: string): Promise<void> {
this.$logger.printMarkdown("Executing initial plugin configuration script...");
const config = this.$options;
const spinner = this.$terminalSpinnerService.createSpinner();
const cwd = path.join(projectDir, "src");
try {
spinner.start();
const npmOptions: any = { silent: true };
await this.$packageManager.install(cwd, cwd, npmOptions);
} finally {
spinner.stop();
}
const gitHubUsername = await this.getGitHubUsername(config.username);
const pluginNameSource = await this.getPluginNameSource(config.pluginName, pluginRepoName);
const includeTypescriptDemo = await this.getShouldIncludeDemoResult(config.includeTypeScriptDemo, this.includeTypeScriptDemoMessage);
const includeAngularDemo = await this.getShouldIncludeDemoResult(config.includeAngularDemo, this.includeAngularDemoMessage);
if (!isInteractive() && (!config.username || !config.pluginName || !config.includeAngularDemo || !config.includeTypeScriptDemo)) {
this.$logger.printMarkdown("Using default values for plugin creation options since your shell is not interactive.");
}
// run postclone script manually and kill it if it takes more than 10 sec
const pathToPostCloneScript = path.join("scripts", "postclone");
const params = [
pathToPostCloneScript,
`gitHubUsername=${gitHubUsername}`,
`pluginName=${pluginNameSource}`,
"initGit=y",
`includeTypeScriptDemo=${includeTypescriptDemo}`,
`includeAngularDemo=${includeAngularDemo}`
];
const outputScript = (await this.$childProcess.spawnFromEvent(process.execPath, params, "close", { stdio: "inherit", cwd, timeout: 10000 }));
if (outputScript && outputScript.stdout) {
this.$logger.printMarkdown(outputScript.stdout);
}
}
private ensurePackageDir(projectDir: string): void {
this.$fs.createDirectory(projectDir);
if (this.$fs.exists(projectDir) && !this.$fs.isEmptyDir(projectDir)) {
this.$errors.fail(this.pathAlreadyExistsMessageTemplate, projectDir);
}
}
private async downloadPackage(selectedTemplate: string, projectDir: string): Promise<void> {
if (selectedTemplate) {
this.$logger.printMarkdown("Make sure your custom template is compatible with the Plugin Seed at https://github.com/NativeScript/nativescript-plugin-seed/");
} else {
this.$logger.printMarkdown("Downloading the latest version of NativeScript Plugin Seed...");
}
const spinner = this.$terminalSpinnerService.createSpinner();
const packageToInstall = selectedTemplate || "https://github.com/NativeScript/nativescript-plugin-seed/archive/master.tar.gz";
try {
spinner.start();
await this.$pacoteService.extractPackage(packageToInstall, projectDir);
} finally {
spinner.stop();
}
}
private async getGitHubUsername(gitHubUsername: string): Promise<string> {
if (!gitHubUsername) {
gitHubUsername = "NativeScriptDeveloper";
if (isInteractive()) {
gitHubUsername = await this.$prompter.getString(this.userMessage, { allowEmpty: false, defaultAction: () => { return gitHubUsername; } });
}
}
return gitHubUsername;
}
private async getPluginNameSource(pluginNameSource: string, pluginRepoName: string): Promise<string> {
if (!pluginNameSource) {
// remove nativescript- prefix for naming plugin files
const prefix = 'nativescript-';
pluginNameSource = pluginRepoName.toLowerCase().startsWith(prefix) ? pluginRepoName.slice(prefix.length, pluginRepoName.length) : pluginRepoName;
if (isInteractive()) {
pluginNameSource = await this.$prompter.getString(this.nameMessage, { allowEmpty: false, defaultAction: () => { return pluginNameSource; } });
}
}
return pluginNameSource;
}
private async getShouldIncludeDemoResult(includeDemoOption: string, message: string): Promise<string> {
let shouldIncludeDemo = !!includeDemoOption;
if (!includeDemoOption && isInteractive()) {
shouldIncludeDemo = await this.$prompter.confirm(message, () => { return true; });
}
return shouldIncludeDemo ? "y" : "n";
}
}
$injector.registerCommand(["plugin|create"], CreatePluginCommand);