mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
⚡ Replace vorpal with oclif in node-dev
This commit is contained in:
parent
be3177a101
commit
cd75c63f83
5
packages/node-dev/bin/run
Executable file
5
packages/node-dev/bin/run
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
require('@oclif/command').run()
|
||||||
|
.then(require('@oclif/command/flush'))
|
||||||
|
.catch(require('@oclif/errors/handle'))
|
3
packages/node-dev/bin/run.cmd
Executable file
3
packages/node-dev/bin/run.cmd
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
node "%~dp0\run" %*
|
|
@ -1,49 +1,59 @@
|
||||||
import Vorpal = require('vorpal');
|
import {
|
||||||
import { Args } from 'vorpal';
|
UserSettings,
|
||||||
|
} from "n8n-core";
|
||||||
|
import { Command, flags } from '@oclif/command';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildFiles,
|
buildFiles,
|
||||||
IBuildOptions,
|
IBuildOptions,
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
import {
|
export class Build extends Command {
|
||||||
UserSettings,
|
static description = 'Builds credentials and nodes and copies it to n8n custom extension folder';
|
||||||
} from "n8n-core";
|
|
||||||
|
|
||||||
module.exports = (vorpal: Vorpal) => {
|
static examples = [
|
||||||
return vorpal
|
`$ n8n-node-dev build`,
|
||||||
.command('build')
|
`$ n8n-node-dev build --destination ~/n8n-nodes`,
|
||||||
// @ts-ignore
|
`$ n8n-node-dev build --watch`,
|
||||||
.description('Builds credentials and nodes and copies it to n8n custom extension folder')
|
];
|
||||||
.option('--destination <folder>',
|
|
||||||
`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) => {
|
|
||||||
|
|
||||||
console.log('\nBuild credentials and nodes');
|
static flags = {
|
||||||
console.log('=========================');
|
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 {
|
async run() {
|
||||||
const options: IBuildOptions = {};
|
const { flags } = this.parse(Build);
|
||||||
|
|
||||||
if (args.options.destination) {
|
this.log('\nBuild credentials and nodes');
|
||||||
options.destinationFolder = args.options.destination;
|
this.log('=========================');
|
||||||
}
|
|
||||||
if (args.options.watch) {
|
|
||||||
options.watch = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const outputDirectory = await buildFiles(options);
|
try {
|
||||||
|
const options: IBuildOptions = {};
|
||||||
|
|
||||||
console.log(`The nodes got build and saved into the following folder:\n${outputDirectory}`);
|
if (flags.destination) {
|
||||||
|
options.destinationFolder = flags.destination;
|
||||||
} catch (error) {
|
}
|
||||||
console.error('\nGOT ERROR');
|
if (flags.watch) {
|
||||||
console.error('====================================');
|
options.watch = true;
|
||||||
console.error(error.message);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import * as changeCase from 'change-case';
|
import * as changeCase from 'change-case';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as inquirer from 'inquirer';
|
import * as inquirer from 'inquirer';
|
||||||
|
import { Command } from '@oclif/command';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import Vorpal = require('vorpal');
|
|
||||||
import { Args } from 'vorpal';
|
|
||||||
|
|
||||||
const { promisify } = require('util');
|
const { promisify } = require('util');
|
||||||
const fsAccess = promisify(fs.access);
|
const fsAccess = promisify(fs.access);
|
||||||
|
@ -12,149 +11,151 @@ import {
|
||||||
createTemplate
|
createTemplate
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
module.exports = (vorpal: Vorpal) => {
|
export class New extends Command {
|
||||||
return vorpal
|
static description = 'Create new credentials/node';
|
||||||
.command('new')
|
|
||||||
// @ts-ignore
|
|
||||||
.description('Create new credentials/node')
|
|
||||||
.action(async (args: Args) => {
|
|
||||||
try {
|
|
||||||
console.log('\nCreate new credentials/node');
|
|
||||||
console.log('=========================');
|
|
||||||
|
|
||||||
// Ask for the type of not to be created
|
static examples = [
|
||||||
const typeQuestion: inquirer.QuestionCollection = {
|
`$ n8n-node-dev new`,
|
||||||
name: 'type',
|
];
|
||||||
|
|
||||||
|
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',
|
type: 'list',
|
||||||
default: 'Node',
|
default: 'Execute',
|
||||||
message: 'What do you want to create?',
|
message: 'What kind of node do you want to create?',
|
||||||
choices: [
|
choices: [
|
||||||
'Credentials',
|
'Execute',
|
||||||
'Node',
|
'Trigger',
|
||||||
|
'Webhook',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const typeAnswers = await inquirer.prompt(typeQuestion);
|
const nodeTypeAnswers = await inquirer.prompt(nodeTypeQuestion);
|
||||||
|
|
||||||
let sourceFolder = '';
|
// Choose a the template-source-file depending on user input.
|
||||||
const sourceFileName = 'simple.ts';
|
sourceFolder = 'execute';
|
||||||
let defaultName = '';
|
defaultName = 'My Node';
|
||||||
let getDescription = false;
|
if (nodeTypeAnswers.nodeType === 'Trigger') {
|
||||||
|
sourceFolder = 'trigger';
|
||||||
|
defaultName = 'My Trigger';
|
||||||
if (typeAnswers.type === 'Node') {
|
} else if (nodeTypeAnswers.nodeType === 'Webhook') {
|
||||||
// Create new node
|
sourceFolder = 'webhook';
|
||||||
|
defaultName = 'My Webhook';
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Create new credentials
|
||||||
|
|
||||||
// Ask additional questions to know with what values the
|
sourceFolder = 'credentials';
|
||||||
// variables in the template file should be replaced with
|
defaultName = 'My Service API';
|
||||||
const additionalQuestions = [
|
}
|
||||||
|
|
||||||
|
// 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',
|
name: 'overwrite',
|
||||||
type: 'input',
|
type: 'confirm',
|
||||||
default: defaultName,
|
default: false,
|
||||||
message: 'How should the node be called?',
|
message: `The file "${destinationFilePath}" already exists and would be overwritten. Do you want to proceed and overwrite the file?`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (getDescription === true) {
|
const overwriteAnswers = await inquirer.prompt(overwriteQuestion);
|
||||||
// Get also a node description
|
|
||||||
additionalQuestions.push({
|
if (overwriteAnswers.overwrite === false) {
|
||||||
name: 'description',
|
this.log('\nNode creation got canceled!');
|
||||||
type: 'input',
|
return;
|
||||||
default: 'Node converts input data to chocolate',
|
|
||||||
message: 'What should the node description be?',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
} catch (error) {
|
||||||
console.error('\nGOT ERROR');
|
// File does not exist. That is exactly what we want so go on.
|
||||||
console.error('====================================');
|
|
||||||
console.error(error.message);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
|
@ -13,6 +13,10 @@
|
||||||
},
|
},
|
||||||
"main": "dist/src/index",
|
"main": "dist/src/index",
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
|
"oclif": {
|
||||||
|
"commands": "./dist/commands",
|
||||||
|
"bin": "run"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run watch",
|
"dev": "npm run watch",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
@ -21,7 +25,7 @@
|
||||||
"watch": "tsc --watch"
|
"watch": "tsc --watch"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"n8n-node-dev": "./dist/index.js"
|
"n8n-node-dev": "./bin/run"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"development",
|
"development",
|
||||||
|
@ -30,6 +34,7 @@
|
||||||
"n8n"
|
"n8n"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
|
"bin",
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -39,13 +44,14 @@
|
||||||
"tslint": "^5.17.0"
|
"tslint": "^5.17.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@oclif/command": "^1.5.18",
|
||||||
|
"@oclif/errors": "^1.2.2",
|
||||||
"change-case": "^3.1.0",
|
"change-case": "^3.1.0",
|
||||||
"inquirer": "^6.3.1",
|
"inquirer": "^6.3.1",
|
||||||
"n8n-core": "^0.2.0",
|
"n8n-core": "^0.2.0",
|
||||||
"n8n-workflow": "^0.3.0",
|
"n8n-workflow": "^0.3.0",
|
||||||
"replace-in-file": "^4.1.0",
|
"replace-in-file": "^4.1.0",
|
||||||
"typescript": "~3.5.2",
|
"typescript": "~3.5.2"
|
||||||
"vorpal": "^1.12.0"
|
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transform": {
|
"transform": {
|
||||||
|
|
Loading…
Reference in a new issue