diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c1230348..916a47ebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,54 @@ +#### 0.18.5: Maintenance Release + +Projects + + - Add clone project to welcome screen + - Handle cloning a project without package.json + - Keep remote branch state in sync between editor and runtime + +New Features + + - Add type checks to switch node options (#1714) + - add output property select to HTML parse node (#1701) + - Add Prevent Following Redirect to HTTP Request node (#615) (#1684) + - Add debug and trace functions to function node (#1654) + - Enable user defined icon for subflow + - Add MQTT disconnect message and rework broker node UI (#1719) + - Japanese message catalogue updates (#1723) + - Show node load errors in the Palette Manager view + +Editor Fixes + + - Highlight subflow node when log msg comes from inside Fixes #1698 + - Ensure node wires array is not longer than outputs value Fixes #1678 + - Allow importing an unknown config node to be undone Fixes #1681 + - Ensure keyboard shortcuts get saved in runtime settings Fixes #1696 + - Don't mark a subflow changed when actually modified nothing (#1665) + +Node Fixes + + - bind to correct port when doing udp broadcast/multicast (#1686) + - Provide full error stack in Function node log message (#1700) + - Fix http request doc type Fixes #1690 + - Make debug slightly larger to pass WCAG AA rating + - Make core nodes labels more consistent, to close #1673 + - Allow template node to be updated more than once Fixes #1671 + - Fix the problem that output labels of switch node sometimes disappear (#1664) + - Chinese translations for core nodes (#1607) + +Runtime Fixes + + - Handle and display for invalid flow credentials when project is disabled #1689 (#1694) + - node-red-pi: fix behavior with old bash version (#1713) + - Fix ENOENT error on first start when no user dir (#1711) + - Handle null error object in Flow.handleError Fixes #1721 + - update settings comments to describe how to setup for ipv6 (#1675) + - Remove credential props after diffing flow to prevent future false positives Fixes #1359 + - Log error if settings unavailable when saving user settings Fixes #1645 + - Keep backup of .config.json + - Add warning if using \_credentialSecret from .config.json + - Filter req.user in /settings to prevent potentially leaking info + #### 0.18.4: Maintenance Release Projects diff --git a/bin/node-red-pi b/bin/node-red-pi index 828e5cca3..0e9de3c49 100755 --- a/bin/node-red-pi +++ b/bin/node-red-pi @@ -31,7 +31,7 @@ done # Find the real location of this script CURRENT_PATH=`pwd` SCRIPT_PATH="${BASH_SOURCE[0]}"; -while([ -h "${SCRIPT_PATH}" ]); do +while [ -h "${SCRIPT_PATH}" ]; do cd "`dirname "${SCRIPT_PATH}"`" SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")"; done diff --git a/editor/js/main.js b/editor/js/main.js index 06d408ca7..b01136386 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - $(function() { - if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) { - document.title = document.title+" : "+window.location.hostname; - } - RED.init(); - }); + +$(function() { + if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) { + document.title = document.title+" : "+window.location.hostname; + } + RED.init(); +}); diff --git a/editor/js/red.js b/editor/js/red.js index 930cd2bdb..74ebb1187 100644 --- a/editor/js/red.js +++ b/editor/js/red.js @@ -214,6 +214,18 @@ var RED = (function() { } ] } + } else if (msg.error === "missing_package_file") { + if (RED.user.hasPermission("projects.write")) { + options.buttons = [ + { + text: "Create default package file", + click: function() { + persistentNotifications[notificationId].hideNotification(); + RED.projects.createDefaultPackageFile(); + } + } + ] + } } else if (msg.error === "project_empty") { if (RED.user.hasPermission("projects.write")) { options.buttons = [ diff --git a/editor/js/ui/palette-editor.js b/editor/js/ui/palette-editor.js index ad24ba826..0793566b2 100644 --- a/editor/js/ui/palette-editor.js +++ b/editor/js/ui/palette-editor.js @@ -208,6 +208,8 @@ RED.palette.editor = (function() { if (nodeEntry) { var activeTypeCount = 0; var typeCount = 0; + var errorCount = 0; + nodeEntry.errorList.empty(); nodeEntries[module].totalUseCount = 0; nodeEntries[module].setUseCount = {}; @@ -216,7 +218,10 @@ RED.palette.editor = (function() { var inUseCount = 0; var set = moduleInfo.sets[setName]; var setElements = nodeEntry.sets[setName]; - + if (set.err) { + errorCount++; + $("
  • ").text(set.err).appendTo(nodeEntry.errorList); + } if (set.enabled) { activeTypeCount += set.types.length; } @@ -255,6 +260,13 @@ RED.palette.editor = (function() { setElements.setRow.toggleClass("palette-module-set-disabled",!set.enabled); } } + + if (errorCount === 0) { + nodeEntry.errorRow.hide() + } else { + nodeEntry.errorRow.show(); + } + var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount; nodeEntry.setCount.html(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount})); @@ -586,6 +598,9 @@ RED.palette.editor = (function() { $('').html(entry.name).appendTo(titleRow); var metaRow = $('
    ').appendTo(headerRow); var versionSpan = $('').html(entry.version).appendTo(metaRow); + + var errorRow = $('
    ').hide().appendTo(headerRow); + var errorList = $('
      ').appendTo(errorRow); var buttonRow = $('
      ',{class:"palette-module-meta"}).appendTo(headerRow); var setButton = $(' ').appendTo(buttonRow); var setCount = $('').appendTo(setButton); @@ -620,6 +635,8 @@ RED.palette.editor = (function() { updateButton: updateButton, removeButton: removeButton, enableButton: enableButton, + errorRow: errorRow, + errorList: errorList, setCount: setCount, container: container, shade: shade, @@ -651,7 +668,6 @@ RED.palette.editor = (function() { typeSwatches[t] = $('',{class:"palette-module-type-swatch"}).appendTo(typeDiv); $('',{class:"palette-module-type-node"}).html(t).appendTo(typeDiv); }) - var enableButton = $('').appendTo(buttonGroup); enableButton.click(function(evt) { evt.preventDefault(); diff --git a/editor/js/ui/projects/projectSettings.js b/editor/js/ui/projects/projectSettings.js index bc882c006..f07f2ba1a 100644 --- a/editor/js/ui/projects/projectSettings.js +++ b/editor/js/ui/projects/projectSettings.js @@ -1255,6 +1255,14 @@ RED.projects.settings = (function() { text: 'Delete remote', click: function() { notification.close(); + + if (activeProject.git.branches.remote && activeProject.git.branches.remote.indexOf(entry.name+"/") === 0) { + delete activeProject.git.branches.remote; + } + if (activeProject.git.branches.remoteAlt && activeProject.git.branches.remoteAlt.indexOf(entry.name+"/") === 0) { + delete activeProject.git.branches.remoteAlt; + } + var url = "projects/"+activeProject.name+"/remotes/"+entry.name; var options = { url: url, @@ -1276,6 +1284,7 @@ RED.projects.settings = (function() { activeProject.git.remotes[name] = remote; }); } + delete activeProject.git.branches.remoteAlt; RED.sidebar.versionControl.refresh(); }); }, diff --git a/editor/js/ui/projects/projects.js b/editor/js/ui/projects/projects.js index 95c2344b7..d7c2787d8 100644 --- a/editor/js/ui/projects/projects.js +++ b/editor/js/ui/projects/projects.js @@ -80,6 +80,26 @@ RED.projects = (function() { $('

      ').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body); $('

      ').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body); + var row = $('

      ').appendTo(body); + var createAsEmpty = $('').appendTo(row); + var createAsClone = $('').appendTo(row); + + createAsEmpty.click(function(e) { + e.preventDefault(); + createProjectOptions = { + action: "create" + } + show('git-config'); + }) + + createAsClone.click(function(e) { + e.preventDefault(); + createProjectOptions = { + action: "clone" + } + show('git-config'); + }) + return container; }, buttons: [ @@ -90,13 +110,6 @@ RED.projects = (function() { createProjectOptions = {}; $( this ).dialog( "close" ); } - }, - { - text: "Create your first project", // TODO: nls - class: "primary", - click: function() { - show('git-config'); - } } ] }, @@ -170,7 +183,11 @@ RED.projects = (function() { currentGitSettings.user.name = gitUsernameInput.val(); currentGitSettings.user.email = gitEmailInput.val(); RED.settings.set('git', currentGitSettings); - show('project-details'); + if (createProjectOptions.action === "create") { + show('project-details'); + } else if (createProjectOptions.action === "clone") { + show('clone-project'); + } } } ] @@ -303,6 +320,366 @@ RED.projects = (function() { } }; })(), + 'clone-project': (function() { + var projectNameInput; + var projectSummaryInput; + var projectFlowFileInput; + var projectSecretInput; + var projectSecretSelect; + var copyProject; + var projectRepoInput; + var projectCloneSecret; + var emptyProjectCredentialInput; + var projectRepoUserInput; + var projectRepoPasswordInput; + var projectNameSublabel; + var projectRepoSSHKeySelect; + var projectRepoPassphrase; + var projectRepoRemoteName + var projectRepoBranch; + var selectedProject; + + return { + content: function(options) { + var container = $('
      '); + migrateProjectHeader.appendTo(container); + var body = $('
      ').appendTo(container); + $('

      ').text("Clone a project").appendTo(body); + $('

      ').text("If you already have a git repository containing a project, you can clone it to get started.").appendTo(body); + + var projectList = null; + var pendingFormValidation = false; + $.getJSON("projects", function(data) { + projectList = {}; + data.projects.forEach(function(p) { + projectList[p] = true; + if (pendingFormValidation) { + pendingFormValidation = false; + validateForm(); + } + }) + }); + + + var validateForm = function() { + var projectName = projectNameInput.val(); + var valid = true; + if (projectNameInputChanged) { + if (projectList === null) { + pendingFormValidation = true; + return; + } + projectNameStatus.empty(); + if (!/^[a-zA-Z0-9\-_]+$/.test(projectName) || projectList[projectName]) { + projectNameInput.addClass("input-error"); + $('').appendTo(projectNameStatus); + projectNameValid = false; + valid = false; + if (projectList[projectName]) { + projectNameSublabel.text("Project already exists"); + } else { + projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + } + } else { + projectNameInput.removeClass("input-error"); + $('').appendTo(projectNameStatus); + projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameValid = true; + } + projectNameLastChecked = projectName; + } + valid = projectNameValid; + + var repo = projectRepoInput.val(); + + // var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo); + var validRepo = repo.length > 0 && !/\s/.test(repo); + if (/^https?:\/\/[^/]+@/i.test(repo)) { + $("#projects-dialog-screen-create-project-repo-label small").text("Do not include the username/password in the url"); + validRepo = false; + } + if (!validRepo) { + if (projectRepoChanged) { + projectRepoInput.addClass("input-error"); + } + valid = false; + } else { + projectRepoInput.removeClass("input-error"); + } + if (/^https?:\/\//.test(repo)) { + $(".projects-dialog-screen-create-row-creds").show(); + $(".projects-dialog-screen-create-row-sshkey").hide(); + } else if (/^(?:ssh|[\S]+?@[\S]+?):(?:\/\/)?/.test(repo)) { + $(".projects-dialog-screen-create-row-creds").hide(); + $(".projects-dialog-screen-create-row-sshkey").show(); + // if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) { + // valid = false; + // } + } else { + $(".projects-dialog-screen-create-row-creds").hide(); + $(".projects-dialog-screen-create-row-sshkey").hide(); + } + + $("#projects-dialog-clone-project").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid); + } + + var row; + + row = $('

      ').appendTo(body); + $('').appendTo(row); + + var subrow = $('
      ').appendTo(row); + projectNameInput = $('').appendTo(subrow); + var projectNameStatus = $('
      ').appendTo(subrow); + + var projectNameInputChanged = false; + var projectNameLastChecked = ""; + var projectNameValid; + var checkProjectName; + var autoInsertedName = ""; + + + projectNameInput.on("change keyup paste",function() { + projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked); + if (checkProjectName) { + clearTimeout(checkProjectName); + } else if (projectNameInputChanged) { + projectNameStatus.empty(); + $('').appendTo(projectNameStatus); + if (projectNameInput.val() === '') { + validateForm(); + return; + } + } + checkProjectName = setTimeout(function() { + validateForm(); + checkProjectName = null; + },300) + }); + projectNameSublabel = $('').appendTo(row).find("small"); + + row = $('
      ').appendTo(body); + $('').appendTo(row); + projectRepoInput = $('').appendTo(row); + $('').appendTo(row); + var projectRepoChanged = false; + var lastProjectRepo = ""; + projectRepoInput.on("change keyup paste",function() { + projectRepoChanged = true; + var repo = $(this).val(); + if (lastProjectRepo !== repo) { + $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + } + lastProjectRepo = repo; + + var m = /\/([^/]+?)(?:\.git)?$/.exec(repo); + if (m) { + var projectName = projectNameInput.val(); + if (projectName === "" || projectName === autoInsertedName) { + autoInsertedName = m[1]; + projectNameInput.val(autoInsertedName); + projectNameInput.change(); + } + } + validateForm(); + }); + + var cloneAuthRows = $('
      ').appendTo(body); + row = $('
      ').hide().appendTo(cloneAuthRows); + $('
      Authentication failed
      ').appendTo(row); + + // Repo credentials - username/password ---------------- + row = $('
      ').hide().appendTo(cloneAuthRows); + + var subrow = $('
      ').appendTo(row); + $('').appendTo(subrow); + projectRepoUserInput = $('').appendTo(subrow); + + subrow = $('
      ').appendTo(row); + $('').appendTo(subrow); + projectRepoPasswordInput = $('').appendTo(subrow); + // ----------------------------------------------------- + + // Repo credentials - key/passphrase ------------------- + row = $('
      ').hide().appendTo(cloneAuthRows); + subrow = $('
      ').appendTo(row); + $('').appendTo(subrow); + projectRepoSSHKeySelect = $("').appendTo(subrow); + + subrow = $('
      ').appendTo(cloneAuthRows); + var sshwarningRow = $('
      ').hide().appendTo(subrow); + $('
      Before you can clone a repository over ssh you must add an SSH key to access it.
      ').appendTo(sshwarningRow); + subrow = $('
      ').appendTo(sshwarningRow); + $('').appendTo(subrow).click(function(e) { + e.preventDefault(); + $('#projects-dialog-cancel').click(); + RED.userSettings.show('gitconfig'); + setTimeout(function() { + $("#user-settings-gitconfig-add-key").click(); + },500); + }); + // ----------------------------------------------------- + + + // Secret - clone + row = $('
      ').appendTo(body); + $('').appendTo(row); + projectSecretInput = $('').appendTo(row); + + + + return container; + }, + buttons: function(options) { + return [ + { + text: "Back", + click: function() { + show('git-config'); + } + }, + { + id: "projects-dialog-clone-project", + disabled: true, + text: "Clone project", // TODO: nls + class: "primary disabled", + click: function() { + var projectType = $(".projects-dialog-screen-create-type.selected").data('type'); + var projectData = { + name: projectNameInput.val(), + } + projectData.credentialSecret = projectSecretInput.val(); + var repoUrl = projectRepoInput.val(); + var metaData = {}; + if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) { + var selected = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect); + if ( selected ) { + projectData.git = { + remotes: { + 'origin': { + url: repoUrl, + keyFile: selected, + passphrase: projectRepoPassphrase.val() + } + } + }; + } + else { + console.log("Error! Can't get selected SSH key path."); + return; + } + } + else { + projectData.git = { + remotes: { + 'origin': { + url: repoUrl, + username: projectRepoUserInput.val(), + password: projectRepoPasswordInput.val() + } + } + }; + } + + $(".projects-dialog-screen-create-row-auth-error").hide(); + $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); + + projectRepoUserInput.removeClass("input-error"); + projectRepoPasswordInput.removeClass("input-error"); + projectRepoSSHKeySelect.removeClass("input-error"); + projectRepoPassphrase.removeClass("input-error"); + + RED.deploy.setDeployInflight(true); + RED.projects.settings.switchProject(projectData.name); + + sendRequest({ + url: "projects", + type: "POST", + handleAuthFail: false, + responses: { + 200: function(data) { + dialog.dialog( "close" ); + }, + 400: { + 'project_exists': function(error) { + console.log("already exists"); + }, + 'git_error': function(error) { + console.log("git error",error); + }, + 'git_connection_failed': function(error) { + projectRepoInput.addClass("input-error"); + $("#projects-dialog-screen-create-project-repo-label small").text("Connection failed"); + }, + 'git_not_a_repository': function(error) { + projectRepoInput.addClass("input-error"); + $("#projects-dialog-screen-create-project-repo-label small").text("Not a git repository"); + }, + 'git_repository_not_found': function(error) { + projectRepoInput.addClass("input-error"); + $("#projects-dialog-screen-create-project-repo-label small").text("Repository not found"); + }, + 'git_auth_failed': function(error) { + $(".projects-dialog-screen-create-row-auth-error").show(); + + projectRepoUserInput.addClass("input-error"); + projectRepoPasswordInput.addClass("input-error"); + // getRepoAuthDetails(req); + projectRepoSSHKeySelect.addClass("input-error"); + projectRepoPassphrase.addClass("input-error"); + }, + 'missing_flow_file': function(error) { + // This is handled via a runtime notification. + dialog.dialog("close"); + }, + 'project_empty': function(error) { + // This is handled via a runtime notification. + dialog.dialog("close"); + }, + 'credentials_load_failed': function(error) { + // This is handled via a runtime notification. + dialog.dialog("close"); + }, + '*': function(error) { + reportUnexpectedError(error); + $( dialog ).dialog( "close" ); + } + } + } + },projectData).then(function() { + RED.events.emit("project:change", {name:name}); + }).always(function() { + setTimeout(function() { + RED.deploy.setDeployInflight(false); + },500); + }) + + } + } + ] + } + } + })(), 'default-files': (function() { var projectFlowFileInput; var projectCredentialFileInput; @@ -1127,6 +1504,7 @@ RED.projects = (function() { } $(".projects-dialog-screen-create-row-auth-error").hide(); + $("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://"); projectRepoUserInput.removeClass("input-error"); projectRepoPasswordInput.removeClass("input-error"); @@ -1873,6 +2251,43 @@ RED.projects = (function() { createProjectOptions = {}; show('default-files',{existingProject: true}); } + function createDefaultPackageFile() { + RED.deploy.setDeployInflight(true); + RED.projects.settings.switchProject(activeProject.name); + + var method = "PUT"; + var url = "projects/"+activeProject.name; + var createProjectOptions = { + initialise: true + }; + sendRequest({ + url: url, + type: method, + requireCleanWorkspace: true, + handleAuthFail: false, + responses: { + 200: function(data) { }, + 400: { + 'git_error': function(error) { + console.log("git error",error); + }, + 'missing_flow_file': function(error) { + // This is a natural next error - but let the runtime event + // trigger the dialog rather than double-report it. + $( dialog ).dialog( "close" ); + }, + '*': function(error) { + reportUnexpectedError(error); + $( dialog ).dialog( "close" ); + } + } + } + },createProjectOptions).always(function() { + setTimeout(function() { + RED.deploy.setDeployInflight(false); + },500); + }) + } function refresh(done) { $.getJSON("projects",function(data) { @@ -1952,6 +2367,7 @@ RED.projects = (function() { RED.projects.settings.show('deps'); }, createDefaultFileSet: createDefaultFileSet, + createDefaultPackageFile: createDefaultPackageFile, // showSidebar: showSidebar, refresh: refresh, editProject: function() { diff --git a/editor/js/ui/projects/tab-versionControl.js b/editor/js/ui/projects/tab-versionControl.js index a31ecb613..44b602019 100644 --- a/editor/js/ui/projects/tab-versionControl.js +++ b/editor/js/ui/projects/tab-versionControl.js @@ -592,7 +592,10 @@ RED.sidebar.versionControl = (function() { closeBranchBox(); localCommitListShade.show(); $(this).addClass('selected'); + var activeProject = RED.projects.getActiveProject(); + $("#sidebar-version-control-repo-toolbar-set-upstream-row").toggle(!!activeProject.git.branches.remoteAlt); remoteBox.show(); + setTimeout(function() { remoteBox.css("height","265px"); },100); @@ -868,7 +871,8 @@ RED.sidebar.versionControl = (function() { if (activeProject.git.branches.remoteAlt) { url+="/"+activeProject.git.branches.remoteAlt; } - if ($("#sidebar-version-control-repo-toolbar-set-upstream").prop('checked')) { + var setUpstream = $("#sidebar-version-control-repo-toolbar-set-upstream").prop('checked'); + if (setUpstream) { url+="?u=true" } utils.sendRequest({ @@ -880,6 +884,10 @@ RED.sidebar.versionControl = (function() { // done(error,null); }, 200: function(data) { + if (setUpstream && activeProject.git.branches.remoteAlt) { + activeProject.git.branches.remote = activeProject.git.branches.remoteAlt; + delete activeProject.git.branches.remoteAlt; + } refresh(true); closeRemoteBox(); }, @@ -928,6 +936,10 @@ RED.sidebar.versionControl = (function() { // done(error,null); }, 200: function(data) { + if (options.setUpstream && activeProject.git.branches.remoteAlt) { + activeProject.git.branches.remote = activeProject.git.branches.remoteAlt; + delete activeProject.git.branches.remoteAlt; + } refresh(true); closeRemoteBox(); }, diff --git a/editor/sass/palette-editor.scss b/editor/sass/palette-editor.scss index b2fb61df3..b99b732b0 100644 --- a/editor/sass/palette-editor.scss +++ b/editor/sass/palette-editor.scss @@ -49,7 +49,12 @@ .palette-module-version { color: #aaa; } - + .palette-module-errors .fa-warning { + opacity: 0.5; + } + ul.palette-module-error-list li { + color: #aaa; + } } @@ -222,6 +227,20 @@ margin-left: 5px; } } +.palette-module-meta .fa-warning { + color: #AD1625; +} +ul.palette-module-error-list { + display: inline-block; + list-style-type: none; + margin: 0; + font-size: 0.9em; + li { + border: none; + background: none; + } +} + .palette-module-shade { @include shade; text-align: center; diff --git a/editor/sass/projects.scss b/editor/sass/projects.scss index d9b2545ac..74fa801c9 100644 --- a/editor/sass/projects.scss +++ b/editor/sass/projects.scss @@ -139,16 +139,17 @@ } } button.editor-button { - width: calc(50% - 40px); + width: calc(50% - 80px); margin: 20px; - height: 175px; + height: auto; line-height: 2em; - font-size: 1.5em !important; + padding: 10px; + border-color: #aaa; i { - color: #ccc; + color: #aaa; } &:hover i { - color: #aaa; + color: #999; } } .button-group { @@ -160,7 +161,6 @@ button.projects-dialog-screen-create-type { height: auto; padding: 10px; - } .button-group { text-align: center; diff --git a/nodes/core/core/80-function.js b/nodes/core/core/80-function.js index 69d6d0ca2..8aa95e4fc 100644 --- a/nodes/core/core/80-function.js +++ b/nodes/core/core/80-function.js @@ -204,7 +204,14 @@ module.exports = function(RED) { } var context = vm.createContext(sandbox); try { - this.script = vm.createScript(functionText); + this.script = vm.createScript(functionText, { + filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces + displayErrors: true + // Using the following options causes node 4/6 to not include the line number + // in the stack output. So don't use them. + // lineOffset: -11, // line number offset to be used for stack traces + // columnOffset: 0, // column number offset to be used for stack traces + }); this.on("input", function(msg) { try { var start = process.hrtime(); @@ -219,6 +226,13 @@ module.exports = function(RED) { this.status({fill:"yellow",shape:"dot",text:""+converted}); } } catch(err) { + //remove unwanted part + var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/); + err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n'); + var stack = err.stack.split(/\r?\n/); + + //store the error in msg to be used in flows + msg.error = err; var line = 0; var errorMessage; diff --git a/nodes/core/io/10-mqtt.html b/nodes/core/io/10-mqtt.html index db3866f28..722a7a7ae 100644 --- a/nodes/core/io/10-mqtt.html +++ b/nodes/core/io/10-mqtt.html @@ -212,48 +212,84 @@
      -