diff --git a/docs/man_pages/project/configuration/generate.md b/docs/man_pages/project/configuration/generate.md new file mode 100644 index 0000000000..3df200a272 --- /dev/null +++ b/docs/man_pages/project/configuration/generate.md @@ -0,0 +1,29 @@ +<% if (isJekyll) { %>--- +title: tns generate +position: 9 +---<% } %> +# tns generate + + +Usage | Synopsis +------|------- +General | `$ tns generate [--collection ] [option=value]` + +Modifies the project by executing a specified schematic to it. + +### Options +* `--collection` - specifies the node package to be used as schematics collection. If it's not specified, `@nativescript/schematics` will be used. + +### Attributes +* `` - name of the schematic to be executed. The schematic should be specified in the used collection. +* `` - options for executed schematic. + +<% if(isHtml) { %> +### Related Commands + +Command | Description +----------|---------- +[update](update.md) | Updates a NativeScript project to the latest (or specified) version. +[resources generate icons](resources-generate-icons.md) | Generates icons for Android and iOS. +[resources generate splashes](resources-generate-splashes.md) | Generates splashscreens for Android and iOS. +<% } %> diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 2788fb7783..21edea90ad 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -42,6 +42,7 @@ $injector.require("emulatorSettingsService", "./services/emulator-settings-servi $injector.require("platformCommandParameter", "./platform-command-param"); $injector.requireCommand("create", "./commands/create-project"); +$injector.requireCommand("generate", "./commands/generate"); $injector.requireCommand("platform|*list", "./commands/list-platforms"); $injector.requireCommand("platform|add", "./commands/add-platform"); $injector.requireCommand("platform|remove", "./commands/remove-platform"); diff --git a/lib/commands/generate.ts b/lib/commands/generate.ts new file mode 100644 index 0000000000..4170f0d54c --- /dev/null +++ b/lib/commands/generate.ts @@ -0,0 +1,84 @@ +import { run, ExecutionOptions } from '@nativescript/schematics-executor'; + +export class GenerateCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + private executionOptions: ExecutionOptions; + + constructor(private $logger: ILogger, + private $options: IOptions, + private $errors: IErrors) { } + + public async execute(_rawArgs: string[]): Promise { + try { + await run(this.executionOptions); + } catch (error) { + this.$errors.failWithoutHelp(error.message); + } + } + + public async canExecute(rawArgs: string[]): Promise { + this.setExecutionOptions(rawArgs); + this.validateExecutionOptions(); + + return true; + } + + private validateExecutionOptions() { + if (!this.executionOptions.schematic) { + this.$errors.fail(`The generate command requires a schematic name to be specified.`); + } + } + + private setExecutionOptions(rawArgs: string[]) { + const options = this.parseRawArgs(rawArgs); + + this.executionOptions = { + ...options, + logger: this.$logger, + directory: process.cwd(), + }; + } + + private parseRawArgs(rawArgs: string[]) { + const collection = this.$options.collection; + const schematic = rawArgs.shift(); + const { options, args } = parseSchematicSettings(rawArgs); + + return { + collection, + schematic, + schematicOptions: options, + schematicArgs: args, + }; + } +} + +/** + * Converts an array of command line arguments to options for the executed schematic. + * @param rawArgs The command line arguments. They should be in the format 'key=value' for strings or 'key' for booleans. + */ +function parseSchematicSettings(rawArgs: string[]) { + const [optionStrings, args] = partition(rawArgs, item => item.includes('=')); + const options = optionStrings + .map(o => o.split("=")) // split to key and value pairs + .map(([key, ...value]) => [key, value.join("=")]) // concat the value arrays if there are multiple = signs + .reduce((obj, [key, value]) => { + return { ...obj, [key]: value }; + }, {}); + + return { options, args }; +} +/** + * Splits an array into two groups based on a predicate. + * @param array The array to split. + * @param predicate The condition to be used for splitting. + */ +function partition(array: T[], predicate: (item: T) => boolean): T[][] { + return array.reduce(([pass, fail], item) => { + return predicate(item) ? + [[...pass, item], fail] : + [pass, [...fail, item]]; + }, [[], []]); +} + +$injector.registerCommand("generate", GenerateCommand); diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 0dfd16b01f..fe040b9c92 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -438,6 +438,10 @@ interface ICreateProjectOptions extends INpmInstallConfigurationOptionsBase { pathToTemplate?: string; } +interface IGenerateOptions { + collection?: string; +} + interface IDebugInformation extends IPort, Mobile.IDeviceIdentifier { url: string; } @@ -451,7 +455,7 @@ interface IPluginSeedOptions { pluginName: string; } -interface IOptions extends ICommonOptions, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions { +interface IOptions extends ICommonOptions, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions { all: boolean; client: boolean; compileSdk: number; diff --git a/lib/options.ts b/lib/options.ts index 3d4422ce06..ffbf2dc4bd 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -41,7 +41,8 @@ export class Options extends commonOptionsLibPath.OptionsBase { background: { type: OptionType.String }, username: { type: OptionType.String }, pluginName: { type: OptionType.String }, - hmr: {type: OptionType.Boolean} + hmr: { type: OptionType.Boolean }, + collection: { type: OptionType.String, alias: "c" }, }, $errors, $staticConfig, $settingsService); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 024a1bd252..cb4746c99f 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@nativescript/schematics-executor": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@nativescript/schematics-executor/-/schematics-executor-0.0.2.tgz", + "integrity": "sha512-3pA0cXbkwu55+He71QO1oRE18wQyquk5t6vpM4laAFatI5w8n2dyHYSzM7pM6bN57zpcP2sk5u1q3YtYv0d2MQ==" + }, "@sinonjs/formatio": { "version": "2.0.0", "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", diff --git a/package.json b/package.json index d465dcb385..f00fc7f83b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "mobile" ], "dependencies": { + "@nativescript/schematics-executor": "0.0.2", "byline": "4.2.1", "chalk": "1.1.0", "chokidar": "1.7.0",