Skip to content

Commit a4cb42c

Browse files
author
Dimitar Tachev
authored
Merge pull request #4625 from NativeScript/vladimirov/merge-rel-master
chore: merge release in master
2 parents 66daa62 + f92d24a commit a4cb42c

12 files changed

+136
-24
lines changed

CHANGELOG.md

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
NativeScript CLI Changelog
22
================
33

4+
5+
5.4.0 (2019, May 15)
6+
==
7+
8+
### Implemented
9+
* [Implemented #3993](https://github.com/NativeScript/nativescript-cli/issues/3993): Improve `ctrl + c` handling.
10+
* [Implemented #4374](https://github.com/NativeScript/nativescript-cli/issues/4374): Add `iCloudContainerEnvironment` build option.
11+
* [Implemented #4394](https://github.com/NativeScript/nativescript-cli/issues/4394): Enable Using Hot Module Replacement by Default for New Projects
12+
* [Implemented #4518](https://github.com/NativeScript/nativescript-cli/issues/4518): Show deprecation messages for things that will be dropped for 6.0.0 release
13+
* [Implemented #4541](https://github.com/NativeScript/nativescript-cli/issues/4541): [Beta] Allow integration of Apple Watch application in NativeScript app
14+
* [Implemented #4548](https://github.com/NativeScript/nativescript-cli/issues/4548): Deprecate support for the Legacy Workflow
15+
* [Implemented #4602](https://github.com/NativeScript/nativescript-cli/issues/4602): Streamline CLI's logger
16+
17+
18+
### Fixed
19+
* [Fixed #4280](https://github.com/NativeScript/nativescript-cli/issues/4280): Incorrect message if you delete app's folder and run command with `--path` in it
20+
* [Fixed #4512](https://github.com/NativeScript/nativescript-cli/issues/4512): App's Podfile should be applied last
21+
* [Fixed #4573](https://github.com/NativeScript/nativescript-cli/pull/4573): logcat process is not restarted in some cases
22+
* [Fixed #4593](https://github.com/NativeScript/nativescript-cli/issues/4593): Node.js processes not killed after `tns create` on macOS when analytics are enabled
23+
* [Fixed #4598](https://github.com/NativeScript/nativescript-cli/issues/4598): app.css changes don't apply when debugging with --debug-brk
24+
* [Fixed #4606](https://github.com/NativeScript/nativescript-cli/issues/4606): Unable to build application for iOS with nativescript-bottombar
25+
* [Fixed #4616](https://github.com/NativeScript/nativescript-cli/issues/4616): `tns plugin create` command hangs
26+
427
5.3.4 (2019, April 24)
528
==
629

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

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Create from a custom plugin seed | `$ tns plugin create <Plugin Repository Name>
2828
* `--path` - Specifies the directory where you want to create the project, if different from the current directory.
2929
* `--username` - Specifies the Github username, which will be used to build the URLs in the plugin's package.json file.
3030
* `--pluginName` - Used to set the default file and class names in the plugin source.
31+
* `--includeTypeScriptDemo` - Specifies if TypeScript demo should be created. Default value is `y` (i.e. `demo` will be created), in case you do not want to create this demo, pass `--includeTypeScriptDemo=n`
32+
* `--includeAngularDemo` - Specifies if Angular demo should be created. Default value is `y` (i.e. `demo-angular` will be created), in case you do not want to create this demo, pass `--includeAngularDemo=n`
3133
* `--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. `<Template>` can be a URL or a local path to a `.tar.gz` file with the contents of a seed repository.<% if(isHtml) { %> 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:
3234

3335
* Using a local file:

lib/commands/plugin/create-plugin.ts

+27-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export class CreatePluginCommand implements ICommand {
55
public allowedParameters: ICommandParameter[] = [];
66
public userMessage = "What is your GitHub username?\n(will be used to update the Github URLs in the plugin's package.json)";
77
public nameMessage = "What will be the name of your plugin?\n(use lowercase characters and dashes only)";
8+
public includeTypeScriptDemoMessage = 'Do you want to include a "TypeScript NativeScript" application linked with your plugin to make development easier?';
9+
public includeAngularDemoMessage = 'Do you want to include an "Angular NativeScript" application linked with your plugin to make development easier?';
810
public pathAlreadyExistsMessageTemplate = "Path already exists and is not empty %s";
911
constructor(private $options: IOptions,
1012
private $errors: IErrors,
@@ -62,15 +64,25 @@ export class CreatePluginCommand implements ICommand {
6264

6365
const gitHubUsername = await this.getGitHubUsername(config.username);
6466
const pluginNameSource = await this.getPluginNameSource(config.pluginName, pluginRepoName);
67+
const includeTypescriptDemo = await this.getShouldIncludeDemoResult(config.includeTypeScriptDemo, this.includeTypeScriptDemoMessage);
68+
const includeAngularDemo = await this.getShouldIncludeDemoResult(config.includeAngularDemo, this.includeAngularDemoMessage);
6569

66-
if (!isInteractive() && (!config.username || !config.pluginName)) {
67-
this.$logger.printMarkdown("Using default values for Github user and/or plugin name since your shell is not interactive.");
70+
if (!isInteractive() && (!config.username || !config.pluginName || !config.includeAngularDemo || !config.includeTypeScriptDemo)) {
71+
this.$logger.printMarkdown("Using default values for plugin creation options since your shell is not interactive.");
6872
}
6973

7074
// run postclone script manually and kill it if it takes more than 10 sec
7175
const pathToPostCloneScript = path.join("scripts", "postclone");
72-
const params = [pathToPostCloneScript, `gitHubUsername=${gitHubUsername}`, `pluginName=${pluginNameSource}`, "initGit=y"];
73-
const outputScript = (await this.$childProcess.spawnFromEvent(process.execPath, params, "close", { cwd, timeout: 10000 }));
76+
const params = [
77+
pathToPostCloneScript,
78+
`gitHubUsername=${gitHubUsername}`,
79+
`pluginName=${pluginNameSource}`,
80+
"initGit=y",
81+
`includeTypeScriptDemo=${includeTypescriptDemo}`,
82+
`includeAngularDemo=${includeAngularDemo}`
83+
];
84+
85+
const outputScript = (await this.$childProcess.spawnFromEvent(process.execPath, params, "close", { stdio: "inherit", cwd, timeout: 10000 }));
7486
if (outputScript && outputScript.stdout) {
7587
this.$logger.printMarkdown(outputScript.stdout);
7688
}
@@ -101,7 +113,7 @@ export class CreatePluginCommand implements ICommand {
101113
}
102114
}
103115

104-
private async getGitHubUsername(gitHubUsername: string) {
116+
private async getGitHubUsername(gitHubUsername: string): Promise<string> {
105117
if (!gitHubUsername) {
106118
gitHubUsername = "NativeScriptDeveloper";
107119
if (isInteractive()) {
@@ -112,7 +124,7 @@ export class CreatePluginCommand implements ICommand {
112124
return gitHubUsername;
113125
}
114126

115-
private async getPluginNameSource(pluginNameSource: string, pluginRepoName: string) {
127+
private async getPluginNameSource(pluginNameSource: string, pluginRepoName: string): Promise<string> {
116128
if (!pluginNameSource) {
117129
// remove nativescript- prefix for naming plugin files
118130
const prefix = 'nativescript-';
@@ -124,6 +136,15 @@ export class CreatePluginCommand implements ICommand {
124136

125137
return pluginNameSource;
126138
}
139+
140+
private async getShouldIncludeDemoResult(includeDemoOption: string, message: string): Promise<string> {
141+
let shouldIncludeDemo = !!includeDemoOption;
142+
if (!includeDemoOption && isInteractive()) {
143+
shouldIncludeDemo = await this.$prompter.confirm(message, () => { return true; });
144+
}
145+
146+
return shouldIncludeDemo ? "y" : "n";
147+
}
127148
}
128149

129150
$injector.registerCommand(["plugin|create"], CreatePluginCommand);

lib/declarations.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ interface IPort {
493493
interface IPluginSeedOptions {
494494
username: string;
495495
pluginName: string;
496+
includeTypeScriptDemo: string;
497+
includeAngularDemo: string;
496498
}
497499

498500
interface IAndroidBundleOptions {

lib/options.ts

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export class Options {
120120
background: { type: OptionType.String, hasSensitiveValue: false },
121121
username: { type: OptionType.String, hasSensitiveValue: true },
122122
pluginName: { type: OptionType.String, hasSensitiveValue: false },
123+
includeTypeScriptDemo: { type: OptionType.String, hasSensitiveValue: false },
124+
includeAngularDemo: { type: OptionType.String, hasSensitiveValue: false },
123125
hmr: { type: OptionType.Boolean, hasSensitiveValue: false },
124126
collection: { type: OptionType.String, alias: "c", hasSensitiveValue: false },
125127
json: { type: OptionType.Boolean, hasSensitiveValue: false },

lib/services/plugins-service.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -318,12 +318,12 @@ export class PluginsService implements IPluginsService {
318318
const installedFrameworkVersion = this.getInstalledFrameworkVersion(platform, projectData);
319319
const pluginPlatformsData = pluginData.platformsData;
320320
if (pluginPlatformsData) {
321-
const pluginVersion = (<any>pluginPlatformsData)[platform];
322-
if (!pluginVersion) {
321+
const versionRequiredByPlugin = (<any>pluginPlatformsData)[platform];
322+
if (!versionRequiredByPlugin) {
323323
this.$logger.warn(`${pluginData.name} is not supported for ${platform}.`);
324324
isValid = false;
325-
} else if (semver.gt(pluginVersion, installedFrameworkVersion)) {
326-
this.$logger.warn(`${pluginData.name} ${pluginVersion} for ${platform} is not compatible with the currently installed framework version ${installedFrameworkVersion}.`);
325+
} else if (semver.gt(versionRequiredByPlugin, installedFrameworkVersion)) {
326+
this.$logger.warn(`${pluginData.name} requires at least version ${versionRequiredByPlugin} of platform ${platform}. Currently installed version is ${installedFrameworkVersion}.`);
327327
isValid = false;
328328
}
329329
}

lib/services/workflow-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { LoggerConfigData } from "../constants";
77
export class WorkflowService implements IWorkflowService {
88
private legacyWorkflowDeprecationMessage = `With the upcoming NativeScript 6.0 the Webpack workflow will become the only way of building apps.
99
More info about the reasons for this change and how to migrate your project can be found in the link below:
10-
<TODO: add link here>`;
10+
https://www.nativescript.org/blog/the-future-of-building-nativescript-apps`;
1111
private webpackWorkflowConfirmMessage = `Do you want to switch your app to the Webpack workflow?`;
1212

1313
constructor(private $bundleValidatorHelper: IBundleValidatorHelper,

npm-shrinkwrap.json

+3-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"minimatch": "3.0.2",
5757
"mkdirp": "0.5.1",
5858
"mute-stream": "0.0.5",
59-
"nativescript-dev-xcode": "https://github.com/NativeScript/nativescript-dev-xcode/tarball/ec70f5d6032a72b65298ad56fa6ec0c4b2ef03a3",
59+
"nativescript-dev-xcode": "0.2.0",
6060
"nativescript-doctor": "1.9.2",
6161
"nativescript-preview-sdk": "0.3.4",
6262
"open": "0.0.5",

test/plugin-create.ts

+60-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const dummyProjectName = "dummyProjectName";
1919
const dummyArgs = [dummyProjectName];
2020
const dummyUser = "devUsername";
2121
const dummyName = "devPlugin";
22+
const createDemoProjectAnswer = true;
23+
const creteDemoProjectOption = "y";
2224
const dummyPacote: IPacoteOutput = { packageName: "", destinationDirectory: "" };
2325

2426
function createTestInjector() {
@@ -104,48 +106,99 @@ describe("Plugin create command tests", () => {
104106
await createPluginCommand.execute(dummyArgs);
105107
});
106108

107-
it("should pass when only project name is set with prompts in interactive shell.", async () => {
109+
it("should pass when all options are set with prompts in interactive shell.", async () => {
108110
const prompter = testInjector.resolve("$prompter");
109111
const strings: IDictionary<string> = {};
112+
const confirmQuestions: IDictionary<boolean> = {};
110113
strings[createPluginCommand.userMessage] = dummyUser;
111114
strings[createPluginCommand.nameMessage] = dummyName;
115+
confirmQuestions[createPluginCommand.includeTypeScriptDemoMessage] = createDemoProjectAnswer;
116+
confirmQuestions[createPluginCommand.includeAngularDemoMessage] = createDemoProjectAnswer;
112117

113118
prompter.expect({
114-
strings: strings
119+
strings: strings,
120+
confirmQuestions
115121
});
116122
await createPluginCommand.execute(dummyArgs);
117123
prompter.assert();
118124
});
119125

120-
it("should pass with project name and username set with one prompt in interactive shell.", async () => {
126+
it("should pass when username is passed with command line option and all other options are populated with prompts in interactive shell.", async () => {
121127
options.username = dummyUser;
122128
const prompter = testInjector.resolve("$prompter");
123129
const strings: IDictionary<string> = {};
130+
const confirmQuestions: IDictionary<boolean> = {};
124131
strings[createPluginCommand.nameMessage] = dummyName;
132+
confirmQuestions[createPluginCommand.includeTypeScriptDemoMessage] = createDemoProjectAnswer;
133+
confirmQuestions[createPluginCommand.includeAngularDemoMessage] = createDemoProjectAnswer;
125134

126135
prompter.expect({
127-
strings: strings
136+
strings: strings,
137+
confirmQuestions
128138
});
129139
await createPluginCommand.execute(dummyArgs);
130140
prompter.assert();
131141
});
132142

133-
it("should pass with project name and pluginName set with one prompt in interactive shell.", async () => {
143+
it("should pass when plugin name is passed with command line option and all other options are populated with prompts in interactive shell.", async () => {
134144
options.pluginName = dummyName;
135145
const prompter = testInjector.resolve("$prompter");
136146
const strings: IDictionary<string> = {};
147+
const confirmQuestions: IDictionary<boolean> = {};
148+
137149
strings[createPluginCommand.userMessage] = dummyUser;
150+
confirmQuestions[createPluginCommand.includeTypeScriptDemoMessage] = createDemoProjectAnswer;
151+
confirmQuestions[createPluginCommand.includeAngularDemoMessage] = createDemoProjectAnswer;
138152

139153
prompter.expect({
140-
strings: strings
154+
strings,
155+
confirmQuestions
141156
});
142157
await createPluginCommand.execute(dummyArgs);
143158
prompter.assert();
144159
});
145160

146-
it("should pass with project name, username and pluginName set with no prompt in interactive shell.", async () => {
161+
it("should pass when includeTypeScriptDemo is passed with command line option and all other options are populated with prompts in interactive shell.", async () => {
162+
options.includeTypeScriptDemo = creteDemoProjectOption;
163+
const prompter = testInjector.resolve("$prompter");
164+
const strings: IDictionary<string> = {};
165+
const confirmQuestions: IDictionary<boolean> = {};
166+
strings[createPluginCommand.userMessage] = dummyUser;
167+
strings[createPluginCommand.nameMessage] = dummyName;
168+
confirmQuestions[createPluginCommand.includeAngularDemoMessage] = createDemoProjectAnswer;
169+
170+
prompter.expect({
171+
strings: strings,
172+
confirmQuestions
173+
});
174+
await createPluginCommand.execute(dummyArgs);
175+
prompter.assert();
176+
});
177+
178+
it("should pass when includeAngularDemo is passed with command line option and all other options are populated with prompts in interactive shell.", async () => {
179+
options.includeAngularDemo = creteDemoProjectOption;
180+
const prompter = testInjector.resolve("$prompter");
181+
const strings: IDictionary<string> = {};
182+
const confirmQuestions: IDictionary<boolean> = {};
183+
184+
strings[createPluginCommand.userMessage] = dummyUser;
185+
strings[createPluginCommand.nameMessage] = dummyName;
186+
confirmQuestions[createPluginCommand.includeTypeScriptDemoMessage] = createDemoProjectAnswer;
187+
188+
prompter.expect({
189+
strings: strings,
190+
confirmQuestions
191+
});
192+
await createPluginCommand.execute(dummyArgs);
193+
prompter.assert();
194+
});
195+
196+
it("should pass with all options passed through command line opts with no prompt in interactive shell.", async () => {
147197
options.username = dummyUser;
148198
options.pluginName = dummyName;
199+
options.includeTypeScriptDemo = creteDemoProjectOption;
200+
options.includeAngularDemo = creteDemoProjectOption;
201+
149202
await createPluginCommand.execute(dummyArgs);
150203
});
151204

test/plugins-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ describe("Plugins service", () => {
272272
});
273273
it("fails when the plugin does not support the installed framework", async () => {
274274
let isWarningMessageShown = false;
275-
const expectedWarningMessage = "mySamplePlugin 1.5.0 for android is not compatible with the currently installed framework version 1.4.0.";
275+
const expectedWarningMessage = "mySamplePlugin requires at least version 1.5.0 of platform android. Currently installed version is 1.4.0.";
276276

277277
// Creates plugin in temp folder
278278
const pluginName = "mySamplePlugin";

test/stubs.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -571,13 +571,15 @@ export class PrompterStub implements IPrompter {
571571
private passwords: IDictionary<string> = {};
572572
private answers: IDictionary<string> = {};
573573
private questionChoices: IDictionary<any[]> = {};
574+
private confirmQuestions: IDictionary<boolean> = {};
574575

575-
expect(options?: { strings?: IDictionary<string>, passwords?: IDictionary<string>, answers?: IDictionary<string>, questionChoices?: IDictionary<any[]> }) {
576+
expect(options?: { strings?: IDictionary<string>, passwords?: IDictionary<string>, answers?: IDictionary<string>, questionChoices?: IDictionary<any[]>, confirmQuestions?: IDictionary<boolean> }) {
576577
if (options) {
577578
this.strings = options.strings || this.strings;
578579
this.passwords = options.passwords || this.passwords;
579580
this.answers = options.answers || this.answers;
580581
this.questionChoices = options.questionChoices || this.questionChoices;
582+
this.confirmQuestions = options.confirmQuestions || this.confirmQuestions;
581583
}
582584
}
583585

@@ -607,7 +609,10 @@ export class PrompterStub implements IPrompter {
607609
return result;
608610
}
609611
async confirm(message: string, defaultAction?: () => boolean): Promise<boolean> {
610-
throw unreachable();
612+
chai.assert.ok(message in this.confirmQuestions, `PrompterStub didn't expect to be asked for: ${message}`);
613+
const result = this.confirmQuestions[message];
614+
delete this.confirmQuestions[message];
615+
return result;
611616
}
612617
dispose(): void {
613618
throw unreachable();
@@ -620,6 +625,9 @@ export class PrompterStub implements IPrompter {
620625
for (const key in this.passwords) {
621626
throw unexpected(`PrompterStub was instructed to reply with "${this.passwords[key]}" to a "${key}" password request, but was never asked!`);
622627
}
628+
for (const key in this.confirmQuestions) {
629+
throw unexpected(`PrompterStub was instructed to reply with "${this.confirmQuestions[key]}" to a "${key}" confirm question, but was never asked!`);
630+
}
623631
}
624632
}
625633

0 commit comments

Comments
 (0)