diff --git a/packages/node-dev/bin/run b/packages/node-dev/bin/run new file mode 100755 index 0000000000..30b14e1773 --- /dev/null +++ b/packages/node-dev/bin/run @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')) diff --git a/packages/node-dev/bin/run.cmd b/packages/node-dev/bin/run.cmd new file mode 100755 index 0000000000..968fc30758 --- /dev/null +++ b/packages/node-dev/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/node-dev/commands/build.ts b/packages/node-dev/commands/build.ts index 0f9f14ba13..0952f07280 100644 --- a/packages/node-dev/commands/build.ts +++ b/packages/node-dev/commands/build.ts @@ -1,49 +1,59 @@ -import Vorpal = require('vorpal'); -import { Args } from 'vorpal'; +import { + UserSettings, +} from "n8n-core"; +import { Command, flags } from '@oclif/command'; + import { buildFiles, IBuildOptions, } from '../src'; -import { - UserSettings, -} from "n8n-core"; +export class Build extends Command { + static description = 'Builds credentials and nodes and copies it to n8n custom extension folder'; -module.exports = (vorpal: Vorpal) => { - return vorpal - .command('build') - // @ts-ignore - .description('Builds credentials and nodes and copies it to n8n custom extension folder') - .option('--destination ', - `The path to copy the compiles files to [default: ${UserSettings.getUserN8nFolderCustomExtensionPath()}]`) - .option('--watch', - 'Starts in watch mode and automatically builds and copies file whenever they change') - .option('\n') - .action(async (args: Args) => { + static examples = [ + `$ n8n-node-dev build`, + `$ n8n-node-dev build --destination ~/n8n-nodes`, + `$ n8n-node-dev build --watch`, + ]; - console.log('\nBuild credentials and nodes'); - console.log('========================='); + static flags = { + help: flags.help({ char: 'h' }), + destination: flags.string({ + char: 'd', + description: `The path to copy the compiles files to [default: ${UserSettings.getUserN8nFolderCustomExtensionPath()}]`, + }), + watch: flags.boolean({ + description: 'Starts in watch mode and automatically builds and copies file whenever they change', + }), + }; - try { - const options: IBuildOptions = {}; + async run() { + const { flags } = this.parse(Build); - if (args.options.destination) { - options.destinationFolder = args.options.destination; - } - if (args.options.watch) { - options.watch = true; - } + this.log('\nBuild credentials and nodes'); + this.log('========================='); - const outputDirectory = await buildFiles(options); + try { + const options: IBuildOptions = {}; - console.log(`The nodes got build and saved into the following folder:\n${outputDirectory}`); - - } catch (error) { - console.error('\nGOT ERROR'); - console.error('===================================='); - console.error(error.message); - return; + if (flags.destination) { + options.destinationFolder = flags.destination; + } + if (flags.watch) { + options.watch = true; } - }); -}; + const outputDirectory = await buildFiles(options); + + this.log(`The nodes got build and saved into the following folder:\n${outputDirectory}`); + + } catch (error) { + this.error('\nGOT ERROR'); + this.error('===================================='); + this.error(error.message); + return; + } + + } +} diff --git a/packages/node-dev/commands/new.ts b/packages/node-dev/commands/new.ts index da0ca1d4fb..abaca67b20 100644 --- a/packages/node-dev/commands/new.ts +++ b/packages/node-dev/commands/new.ts @@ -1,9 +1,8 @@ import * as changeCase from 'change-case'; import * as fs from 'fs'; import * as inquirer from 'inquirer'; +import { Command } from '@oclif/command'; import { join } from 'path'; -import Vorpal = require('vorpal'); -import { Args } from 'vorpal'; const { promisify } = require('util'); const fsAccess = promisify(fs.access); @@ -12,149 +11,151 @@ import { createTemplate } from '../src'; -module.exports = (vorpal: Vorpal) => { - return vorpal - .command('new') - // @ts-ignore - .description('Create new credentials/node') - .action(async (args: Args) => { - try { - console.log('\nCreate new credentials/node'); - console.log('========================='); +export class New extends Command { + static description = 'Create new credentials/node'; - // Ask for the type of not to be created - const typeQuestion: inquirer.QuestionCollection = { - name: 'type', + static examples = [ + `$ n8n-node-dev new`, + ]; + + async run() { + + try { + this.log('\nCreate new credentials/node'); + this.log('========================='); + + // Ask for the type of not to be created + const typeQuestion: inquirer.Question = { + name: 'type', + type: 'list', + default: 'Node', + message: 'What do you want to create?', + choices: [ + 'Credentials', + 'Node', + ], + }; + + const typeAnswers = await inquirer.prompt(typeQuestion); + + let sourceFolder = ''; + const sourceFileName = 'simple.ts'; + let defaultName = ''; + let getDescription = false; + + + if (typeAnswers.type === 'Node') { + // Create new node + + getDescription = true; + + const nodeTypeQuestion: inquirer.Question = { + name: 'nodeType', type: 'list', - default: 'Node', - message: 'What do you want to create?', + default: 'Execute', + message: 'What kind of node do you want to create?', choices: [ - 'Credentials', - 'Node', + 'Execute', + 'Trigger', + 'Webhook', ], }; - const typeAnswers = await inquirer.prompt(typeQuestion); + const nodeTypeAnswers = await inquirer.prompt(nodeTypeQuestion); - let sourceFolder = ''; - const sourceFileName = 'simple.ts'; - let defaultName = ''; - let getDescription = false; - - - if (typeAnswers.type === 'Node') { - // Create new node - - getDescription = true; - - const nodeTypeQuestion: inquirer.QuestionCollection = { - name: 'nodeType', - type: 'list', - default: 'Execute', - message: 'What kind of node do you want to create?', - choices: [ - 'Execute', - 'Trigger', - 'Webhook', - ], - }; - - const nodeTypeAnswers = await inquirer.prompt(nodeTypeQuestion); - - // Choose a the template-source-file depending on user input. - sourceFolder = 'execute'; - defaultName = 'My Node'; - if (nodeTypeAnswers.nodeType === 'Trigger') { - sourceFolder = 'trigger'; - defaultName = 'My Trigger'; - } else if (nodeTypeAnswers.nodeType === 'Webhook') { - sourceFolder = 'webhook'; - defaultName = 'My Webhook'; - } - } else { - // Create new credentials - - sourceFolder = 'credentials'; - defaultName = 'My Service API'; + // Choose a the template-source-file depending on user input. + sourceFolder = 'execute'; + defaultName = 'My Node'; + if (nodeTypeAnswers.nodeType === 'Trigger') { + sourceFolder = 'trigger'; + defaultName = 'My Trigger'; + } else if (nodeTypeAnswers.nodeType === 'Webhook') { + sourceFolder = 'webhook'; + defaultName = 'My Webhook'; } + } else { + // Create new credentials - // Ask additional questions to know with what values the - // variables in the template file should be replaced with - const additionalQuestions = [ + sourceFolder = 'credentials'; + defaultName = 'My Service API'; + } + + // Ask additional questions to know with what values the + // variables in the template file should be replaced with + const additionalQuestions = [ + { + name: 'name', + type: 'input', + default: defaultName, + message: 'How should the node be called?', + }, + ]; + + if (getDescription === true) { + // Get also a node description + additionalQuestions.push({ + name: 'description', + type: 'input', + default: 'Node converts input data to chocolate', + message: 'What should the node description be?', + }); + } + + const additionalAnswers = await inquirer.prompt(additionalQuestions as inquirer.Questions); + + const nodeName = additionalAnswers.name; + + // Define the source file to be used and the location and name of the new + // node file + const destinationFilePath = join(process.cwd(), `${changeCase.pascalCase(nodeName)}.${typeAnswers.type.toLowerCase()}.ts`); + + const sourceFilePath = join(__dirname, '../../templates', sourceFolder, sourceFileName); + + // Check if node with the same name already exists in target folder + // to not overwrite it by accident + try { + await fsAccess(destinationFilePath); + + // File does already exist. So ask if it should be overwritten. + const overwriteQuestion: inquirer.Questions = [ { - name: 'name', - type: 'input', - default: defaultName, - message: 'How should the node be called?', + name: 'overwrite', + type: 'confirm', + default: false, + message: `The file "${destinationFilePath}" already exists and would be overwritten. Do you want to proceed and overwrite the file?`, }, ]; - if (getDescription === true) { - // Get also a node description - additionalQuestions.push({ - name: 'description', - type: 'input', - default: 'Node converts input data to chocolate', - message: 'What should the node description be?', - }); + const overwriteAnswers = await inquirer.prompt(overwriteQuestion); + + if (overwriteAnswers.overwrite === false) { + this.log('\nNode creation got canceled!'); + return; } - - const additionalAnswers = await inquirer.prompt(additionalQuestions as inquirer.QuestionCollection); - - const nodeName = additionalAnswers.name; - - // Define the source file to be used and the location and name of the new - // node file - const destinationFilePath = join(process.cwd(), `${changeCase.pascalCase(nodeName)}.${typeAnswers.type.toLowerCase()}.ts`); - - const sourceFilePath = join(__dirname, '../../templates', sourceFolder, sourceFileName); - - // Check if node with the same name already exists in target folder - // to not overwrite it by accident - try { - await fsAccess(destinationFilePath); - - // File does already exist. So ask if it should be overwritten. - const overwriteQuestion: inquirer.QuestionCollection = [ - { - name: 'overwrite', - type: 'confirm', - default: false, - message: `The file "${destinationFilePath}" already exists and would be overwritten. Do you want to proceed and overwrite the file?`, - }, - ]; - - const overwriteAnswers = await inquirer.prompt(overwriteQuestion); - - if (overwriteAnswers.overwrite === false) { - console.log('\nNode creation got canceled!'); - return; - } - } catch (error) { - // File does not exist. That is exactly what we want so go on. - } - - // Make sure that the variables in the template file get formated - // in the correct way - const replaceValues = { - ClassNameReplace: changeCase.pascalCase(nodeName), - DisplayNameReplace: changeCase.titleCase(nodeName), - N8nNameReplace: changeCase.camelCase(nodeName), - NodeDescriptionReplace: additionalAnswers.description, - }; - - await createTemplate(sourceFilePath, destinationFilePath, replaceValues); - - console.log('\nExecution was successfull:'); - console.log('===================================='); - - console.log('Node got created: ' + destinationFilePath); } catch (error) { - console.error('\nGOT ERROR'); - console.error('===================================='); - console.error(error.message); - return; + // File does not exist. That is exactly what we want so go on. } - }); -}; + // Make sure that the variables in the template file get formated + // in the correct way + const replaceValues = { + ClassNameReplace: changeCase.pascalCase(nodeName), + DisplayNameReplace: changeCase.titleCase(nodeName), + N8nNameReplace: changeCase.camelCase(nodeName), + NodeDescriptionReplace: additionalAnswers.description, + }; + + await createTemplate(sourceFilePath, destinationFilePath, replaceValues); + + this.log('\nExecution was successfull:'); + this.log('===================================='); + + this.log('Node got created: ' + destinationFilePath); + } catch (error) { + this.error('\nGOT ERROR'); + this.error('===================================='); + this.error(error.message); + return; + } + } +} diff --git a/packages/node-dev/index.ts b/packages/node-dev/index.ts deleted file mode 100644 index bee372215e..0000000000 --- a/packages/node-dev/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env node - -import Vorpal = require('vorpal'); - -if (process.argv.length === 2) { - // When no command is given choose by default help - process.argv.push('help'); -} - -const command = process.argv[2]; - -// Check if the command the user did enter is supported else stop -const supportedCommands = [ - 'build', - 'help', - 'new', -]; - -if (!supportedCommands.includes(command)) { - console.log(`The command "${command}" is not known!`); - process.argv.push('help'); -} - -const vorpal = new Vorpal(); -vorpal - .use(require('./commands/build.js')) - .use(require('./commands/new.js')) - .delimiter('') - .show() - .parse(process.argv); - - -process - .on('unhandledRejection', (reason, p) => { - console.error(reason, 'Unhandled Rejection at Promise', p); - }) - .on('uncaughtException', err => { - console.error(err, 'Uncaught Exception thrown'); - process.exit(1); - }); diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index b1ecdb2fac..a6cb23fbf3 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -13,6 +13,10 @@ }, "main": "dist/src/index", "types": "dist/src/index.d.ts", + "oclif": { + "commands": "./dist/commands", + "bin": "run" + }, "scripts": { "dev": "npm run watch", "build": "tsc", @@ -21,7 +25,7 @@ "watch": "tsc --watch" }, "bin": { - "n8n-node-dev": "./dist/index.js" + "n8n-node-dev": "./bin/run" }, "keywords": [ "development", @@ -30,6 +34,7 @@ "n8n" ], "files": [ + "bin", "dist" ], "devDependencies": { @@ -39,13 +44,14 @@ "tslint": "^5.17.0" }, "dependencies": { + "@oclif/command": "^1.5.18", + "@oclif/errors": "^1.2.2", "change-case": "^3.1.0", "inquirer": "^6.3.1", "n8n-core": "^0.2.0", "n8n-workflow": "^0.3.0", "replace-in-file": "^4.1.0", - "typescript": "~3.5.2", - "vorpal": "^1.12.0" + "typescript": "~3.5.2" }, "jest": { "transform": {