Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/getswagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var fs = require('fs');
var request = require('request');

request("http://petstore.swagger.io/v2/swagger.json", function (error, response, body) {
"use strict";
if (!error) {
try {
var swagger = JSON.parse(body);
Expand Down
11 changes: 7 additions & 4 deletions bin/node-red-nodegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ var data = {
version: argv.version,
keywords: argv.keywords || argv.k,
category: argv.category || argv.c,
icon: argv.icon,
color: argv.color,
dst: argv.output || argv.o || '.'
};

function help() {
"use strict";
var helpText = 'Usage:'.bold + '\n' +
' node-red-nodegen <source file or URL>' +
' [-o <path to save>]' +
Expand All @@ -49,8 +52,8 @@ function help() {
' [--version <version number>]' +
' [--keywords <keywords list>]' +
' [--category <node category>]' +
//' [--icon <png or gif file>]' +
//' [--color <node color>]' +
' [--icon <png or gif file>]' +
' [--color <node color>]' +
' [--tgz]' +
' [--help]' +
' [-v]\n' +
Expand All @@ -71,8 +74,8 @@ function help() {
' --version : Node version (format: "number.number.number" like "4.5.1")\n' +
' --keywords : Additional keywords (format: comma separated string, default: "node-red-nodegen")\n' +
' --category : Node category (default: "function")\n' +
//' --icon : png or gif file for node appearance (image size should be 10x20)\n';
//' --color : color for node appearance (format: color hexadecimal numbers like "#A6BBCF")\n';
' --icon : png or gif file for node appearance (image size should be 10x20)\n' +
' --color : color for node appearance (format: color hexadecimal numbers like "A6BBCF")\n' +
' --tgz : Save node as tgz file\n' +
' --help : Show help\n' +
' -v : Show node generator version\n';
Expand Down
111 changes: 96 additions & 15 deletions lib/nodegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@

var when = require("when");
var fs = require('fs');
var path = require('path');
var child_process = require('child_process');
var request = require('request');
var mustache = require('mustache');
var jsStringEscape = require('js-string-escape');
var obfuscator = require('javascript-obfuscator');
var csv = require('csv-string');
var path = require('path');
var CodeGen = require('swagger-js-codegen').CodeGen;
var jimp = require("jimp");

function createCommonFiles(templateDirectory, data) {
"use strict";
// Make directories
try {
fs.mkdirSync(path.join(data.dst, data.module));
Expand All @@ -34,14 +36,38 @@ function createCommonFiles(templateDirectory, data) {
console.error(error);
}
}
try {
fs.mkdirSync(path.join(data.dst, data.module, 'icons'));
} catch (error) {
if (error.code !== 'EEXIST') {
console.error(error);

var isStockIcon = data.icon && data.icon.match(/^(alert|arduino|arrow-in|batch|bluetooth|bridge-dash|bridge|cog|comment|db|debug|envelope|feed|file-in|file-out|file|function|hash|inject|join|leveldb|light|link-out|mongodb|mouse|node-changed|node-error|parser-csv|parser-html|parser-json|parser-xml|parser-yaml|range|redis|rpi|serial|sort|split|subflow|swap|switch|template|timer|trigger|twitter|watch|white-globe)\.png$/);
if (!isStockIcon) {
try {
fs.mkdirSync(path.join(data.dst, data.module, 'icons'));
} catch (error) {
if (error.code !== 'EEXIST') {
console.error(error);
}
}
}
try {
if (data.icon) {
if (!isStockIcon) {
try {
jimp.read(data.icon, function (error2, image) {
if (!error2) {
var outputPath = path.join(data.dst, data.module, 'icons', path.basename(data.icon));
if (image.bitmap.width === 20 && image.bitmap.height === 30) {
var buf = fs.readFileSync(data.icon);
fs.writeFileSync(outputPath, buf);
} else {
image.background(0xFFFFFFFF).resize(20, 30).write(outputPath);
}
} else {
console.log('error occurs while converting icon file.');
}
});
} catch (error) {
console.error(error);
}
}
} else {
var icons = fs.readdirSync(path.join(templateDirectory, 'icons'));
icons.forEach(function (icon) {
try {
Expand All @@ -51,11 +77,8 @@ function createCommonFiles(templateDirectory, data) {
console.error(error);
}
});
} catch (error) {
if (error.code !== 'ENOENT') {
console.error(error);
}
}

try {
fs.mkdirSync(path.join(data.dst, data.module, 'locales'));
} catch (error) {
Expand Down Expand Up @@ -89,6 +112,7 @@ function createCommonFiles(templateDirectory, data) {
}

function runNpmPack(data) {
"use strict";
var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
try {
child_process.execFileSync(npmCommand, ['pack', './' + data.module], { cwd: data.dst });
Expand All @@ -98,6 +122,7 @@ function runNpmPack(data) {
}

function extractKeywords(keywordsStr) {
"use strict";
var keywords = ["node-red-nodegen"];
keywords = keywordsStr ? keywords.concat(csv.parse(keywordsStr)[0]) : keywords;
keywords = keywords.map(k => ({ name: k }));
Expand All @@ -106,6 +131,7 @@ function extractKeywords(keywordsStr) {
}

function function2node(data, options) {
"use strict";
return when.promise(function (resolve, reject) {
// Read meta data in js file
var meta = {};
Expand Down Expand Up @@ -142,6 +168,25 @@ function function2node(data, options) {
data.version = '0.0.1';
}

if (data.icon) {
if (!data.icon.match(/\.(png|gif)$/)) {
data.icon = data.icon + '.png';
}
if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) {
reject('invalid icon file name');
return;
}
}

if (data.color) {
if (data.color.match(/^[a-zA-Z0-9]{6}$/)) {
data.color = '#' + data.color;
} else {
reject('invalid color');
return;
}
}

if (data.name === 'function') {
reject('\'function\' is duplicated node name. Use another name.');
return;
Expand All @@ -155,10 +200,18 @@ function function2node(data, options) {
projectVersion: data.version,
keywords: extractKeywords(data.keywords),
category: data.category || 'function',
icon: function () {
if (data.icon) {
return path.basename(data.icon);
} else {
return 'icon.png';
}
},
color: data.color || '#C0DEED',
func: jsStringEscape(data.src),
outputs: meta.outputs
};

createCommonFiles(__dirname + '/../templates/function', data);

// Create package.json
Expand Down Expand Up @@ -205,6 +258,7 @@ function function2node(data, options) {
}

function swagger2node(data, options) {
"use strict";
return when.promise(function (resolve, reject) {
// Modify swagger data
var swagger = JSON.parse(JSON.stringify(data.src), function (key, value) {
Expand Down Expand Up @@ -272,6 +326,25 @@ function swagger2node(data, options) {
}
}

if (data.icon) {
if (!data.icon.match(/\.(png|gif)$/)) {
data.icon = data.icon + '.png';
}
if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) {
reject('invalid icon file name');
return;
}
}

if (data.color) {
if (data.color.match(/^[a-zA-Z0-9]{6}$/)) {
data.color = '#' + data.color;
} else {
reject('invalid color');
return;
}
}

createCommonFiles(path.join(__dirname, '../templates/swagger'), data);

// Create Node.js SDK
Expand Down Expand Up @@ -318,18 +391,18 @@ function swagger2node(data, options) {
var isNotBodyParam = function () {
return function (content, render) {
return render('{{camelCaseName}}') !== 'body' ? render(content) : '';
}
};
};
var isBodyParam = function () {
return function (content, render) {
return render('{{camelCaseName}}') === 'body' ? render(content) : '';
}
};
};
var hasOptionalParams = function () {
return function (content, render) {
var params = render('{{#parameters}}{{^required}}{{camelCaseName}},{{/required}}{{/parameters}}');
return params.split(',').filter(p => p).some(p => p !== 'body') ? render(content) : '';
}
};
};
var hasServiceParams = swagger.host === undefined || swagger.security !== undefined;

Expand Down Expand Up @@ -368,6 +441,14 @@ function swagger2node(data, options) {
mustache: {
nodeName: data.name,
category: data.category || 'function',
icon: function () {
if (data.icon) {
return path.basename(data.icon);
} else {
return 'icon.png';
}
},
color: data.color || '#89bf04',
isNotBodyParam: isNotBodyParam,
hasOptionalParams: hasOptionalParams,
hasServiceParams: hasServiceParams
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"colors": "1.1.2",
"csv-string": "3.1.2",
"javascript-obfuscator": "0.16.0",
"jimp": "0.2.28",
"js-string-escape": "1.0.1",
"minimist": "1.2.0",
"mustache": "2.3.0",
Expand All @@ -85,18 +86,18 @@
"yamljs": "0.3.0"
},
"devDependencies": {
"del": "3.0.0",
"grunt": "1.0.1",
"grunt-mocha-istanbul": "5.0.2",
"grunt-shell": "2.1.0",
"grunt-simple-mocha": "0.4.1",
"grunt-mocha-istanbul": "5.0.2",
"istanbul": "0.4.5",
"should": "13.1.3",
"node-red": "0.18.7",
"node-red": "0.18.4",
"node-red-node-test-helper": "0.1.8",
"q": "1.5.1",
"should": "13.1.3",
"sinon": "4.1.3",
"supertest": "3.0.0",
"del": "3.0.0",
"q": "1.5.1"
"supertest": "3.0.0"
},
"bin": {
"node-red-nodegen": "./bin/node-red-nodegen.js"
Expand Down
6 changes: 3 additions & 3 deletions templates/function/node.html.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@

<script type="text/javascript">
RED.nodes.registerType('{{&nodeName}}',{
color:"#C0DEED",
color: '{{&color}}',
category: '{{&category}}',
defaults: {
name: {value:""},
name: { value: '' },
},
inputs:1,
outputs: {{&outputs}},
icon: "icon.png",
icon: '{{&icon}}',
label: function() {
return this.name || '{{&nodeName}}';
}
Expand Down
8 changes: 3 additions & 5 deletions templates/swagger/node.html.mustache
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
<script type="text/javascript">
RED.nodes.registerType('{{&nodeName}}', {
category: '{{&category}}',
color: '#89bf04',
color: '{{&color}}',
defaults: {
{{#hasServiceParams}}
service: { value: "", type: "{{&nodeName}}-service", required: true },
{{/hasServiceParams}}
method: { value: "" },

method: { value: "", required: true },
{{#methods}}
{{#parameters}}
{{&methodName}}_{{&camelCaseName}}: { value: "" },
{{&methodName}}_{{&camelCaseName}}Type: { value: "str" },
{{/parameters}}
{{/methods}}

name: { value: "" }
},
inputs: 1,
outputs: 1,
icon: "icon.png",
icon: '{{&icon}}',
label: function () {
return this.name || "{{&nodeName}}";
},
Expand Down