소스 검색

Merge pull request #157 from icyblazek/master

许雪里 8 년 전
부모
커밋
145a900683

+ 1 - 0
xxl-job-admin/src/main/webapp/WEB-INF/template/jobcode/jobcode.index.ftl 파일 보기

@@ -113,6 +113,7 @@
113 113
 <script src="${request.contextPath}/static/plugins/codemirror/mode/clike/clike.js"></script>
114 114
 <script src="${request.contextPath}/static/plugins/codemirror/mode/shell/shell.js"></script>
115 115
 <script src="${request.contextPath}/static/plugins/codemirror/mode/python/python.js"></script>
116
+<script src="${request.contextPath}/static/plugins/codemirror/mode/javascript/javascript.js"></script>
116 117
 <script src="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.js"></script>
117 118
 <script src="${request.contextPath}/static/plugins/codemirror/addon/hint/anyword-hint.js"></script>
118 119
 <script>

+ 11 - 1
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl 파일 보기

@@ -233,7 +233,17 @@ logging.basicConfig(level=logging.DEBUG)
233 233
 logging.info("脚本文件:" + sys.argv[0])
234 234
 -->
235 235
 </textarea>
236
-					
236
+<textarea class="glueSource_nodejs" style="display:none;" >
237
+// #!/usr/bin/node
238
+console.log("xxl-job: hello nodejs")
239
+var arguments = process.argv
240
+console.log("脚本文件: " + arguments[1])
241
+for (var i = 2; i < arguments.length; i++){
242
+	console.log("参数: %s, %s", i, arguments[i]);
243
+}
244
+console.log("Good bye!")
245
+process.exit(0)
246
+</textarea>		
237 247
 				</form>
238 248
          	</div>
239 249
 		</div>

+ 2 - 0
xxl-job-admin/src/main/webapp/static/js/jobcode.index.1.js 파일 보기

@@ -16,6 +16,8 @@ $(function() {
16 16
 			ideMode = "text/x-sh";
17 17
 		} else if ('GLUE_PYTHON'==glueType){
18 18
 			ideMode = "text/x-python";
19
+		} else if ('GLUE_NODEJS'==glueType){
20
+			ideMode = "text/javascript"
19 21
 		}
20 22
 
21 23
 		codeEditor = CodeMirror(document.getElementById("ideWindow"), {

+ 4 - 0
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js 파일 보기

@@ -55,6 +55,8 @@ $(function() {
55 55
 								return "GLUE模式(Shell)";
56 56
 							} else if ('GLUE_PYTHON'==row.glueType) {
57 57
 								return "GLUE模式(Python)";
58
+							}else if  ('GLUE_NODEJS'==row.glueType){
59
+								return "GLUE模式(Nodejs)";
58 60
 							} else if ('BEAN'==row.glueType) {
59 61
 								return "BEAN模式:" + row.executorHandler;
60 62
 							}
@@ -341,6 +343,8 @@ $(function() {
341 343
 			$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_shell").val() );
342 344
 		} else if ('GLUE_PYTHON'==glueType){
343 345
 			$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_python").val() );
346
+		} else if ('GLUE_NODEJS'==glueType){
347
+			$("#addModal .form textarea[name='glueSource']").val( $("#addModal .form .glueSource_nodejs").val() );			
344 348
 		}
345 349
 	});
346 350
 

+ 2 - 0
xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js 파일 보기

@@ -105,6 +105,8 @@ $(function() {
105 105
                                 glueTypeTitle = "GLUE模式(Shell)";
106 106
                             } else if ('GLUE_PYTHON'==row.glueType) {
107 107
                                 glueTypeTitle = "GLUE模式(Python)";
108
+                            }else if ('GLUE_NODEJS'==row.glueType) {
109
+                            	glueTypeTitle = "GLUE模式(Nodejs)";
108 110
                             } else if ('BEAN'==row.glueType) {
109 111
                                 glueTypeTitle = "BEAN模式:" + row.executorHandler;
110 112
                             }

+ 838 - 0
xxl-job-admin/src/main/webapp/static/plugins/codemirror/mode/javascript/javascript.js 파일 보기

@@ -0,0 +1,838 @@
1
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+// Distributed under an MIT license: http://codemirror.net/LICENSE
3
+
4
+(function(mod) {
5
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+    mod(require("../../lib/codemirror"));
7
+  else if (typeof define == "function" && define.amd) // AMD
8
+    define(["../../lib/codemirror"], mod);
9
+  else // Plain browser env
10
+    mod(CodeMirror);
11
+})(function(CodeMirror) {
12
+"use strict";
13
+
14
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
15
+  var indentUnit = config.indentUnit;
16
+  var statementIndent = parserConfig.statementIndent;
17
+  var jsonldMode = parserConfig.jsonld;
18
+  var jsonMode = parserConfig.json || jsonldMode;
19
+  var isTS = parserConfig.typescript;
20
+  var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
21
+
22
+  // Tokenizer
23
+
24
+  var keywords = function(){
25
+    function kw(type) {return {type: type, style: "keyword"};}
26
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
27
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
28
+
29
+    var jsKeywords = {
30
+      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
31
+      "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
32
+      "var": kw("var"), "const": kw("var"), "let": kw("var"),
33
+      "function": kw("function"), "catch": kw("catch"),
34
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
35
+      "in": operator, "typeof": operator, "instanceof": operator,
36
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
37
+      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
38
+      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
39
+      "await": C
40
+    };
41
+
42
+    // Extend the 'normal' keywords with the TypeScript language extensions
43
+    if (isTS) {
44
+      var type = {type: "variable", style: "type"};
45
+      var tsKeywords = {
46
+        // object-like things
47
+        "interface": kw("class"),
48
+        "implements": C,
49
+        "namespace": C,
50
+        "module": kw("module"),
51
+        "enum": kw("module"),
52
+
53
+        // scope modifiers
54
+        "public": kw("modifier"),
55
+        "private": kw("modifier"),
56
+        "protected": kw("modifier"),
57
+        "abstract": kw("modifier"),
58
+        "readonly": kw("modifier"),
59
+
60
+        // types
61
+        "string": type, "number": type, "boolean": type, "any": type
62
+      };
63
+
64
+      for (var attr in tsKeywords) {
65
+        jsKeywords[attr] = tsKeywords[attr];
66
+      }
67
+    }
68
+
69
+    return jsKeywords;
70
+  }();
71
+
72
+  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
73
+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
74
+
75
+  function readRegexp(stream) {
76
+    var escaped = false, next, inSet = false;
77
+    while ((next = stream.next()) != null) {
78
+      if (!escaped) {
79
+        if (next == "/" && !inSet) return;
80
+        if (next == "[") inSet = true;
81
+        else if (inSet && next == "]") inSet = false;
82
+      }
83
+      escaped = !escaped && next == "\\";
84
+    }
85
+  }
86
+
87
+  // Used as scratch variables to communicate multiple values without
88
+  // consing up tons of objects.
89
+  var type, content;
90
+  function ret(tp, style, cont) {
91
+    type = tp; content = cont;
92
+    return style;
93
+  }
94
+  function tokenBase(stream, state) {
95
+    var ch = stream.next();
96
+    if (ch == '"' || ch == "'") {
97
+      state.tokenize = tokenString(ch);
98
+      return state.tokenize(stream, state);
99
+    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
100
+      return ret("number", "number");
101
+    } else if (ch == "." && stream.match("..")) {
102
+      return ret("spread", "meta");
103
+    } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
104
+      return ret(ch);
105
+    } else if (ch == "=" && stream.eat(">")) {
106
+      return ret("=>", "operator");
107
+    } else if (ch == "0" && stream.eat(/x/i)) {
108
+      stream.eatWhile(/[\da-f]/i);
109
+      return ret("number", "number");
110
+    } else if (ch == "0" && stream.eat(/o/i)) {
111
+      stream.eatWhile(/[0-7]/i);
112
+      return ret("number", "number");
113
+    } else if (ch == "0" && stream.eat(/b/i)) {
114
+      stream.eatWhile(/[01]/i);
115
+      return ret("number", "number");
116
+    } else if (/\d/.test(ch)) {
117
+      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
118
+      return ret("number", "number");
119
+    } else if (ch == "/") {
120
+      if (stream.eat("*")) {
121
+        state.tokenize = tokenComment;
122
+        return tokenComment(stream, state);
123
+      } else if (stream.eat("/")) {
124
+        stream.skipToEnd();
125
+        return ret("comment", "comment");
126
+      } else if (expressionAllowed(stream, state, 1)) {
127
+        readRegexp(stream);
128
+        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
129
+        return ret("regexp", "string-2");
130
+      } else {
131
+        stream.eatWhile(isOperatorChar);
132
+        return ret("operator", "operator", stream.current());
133
+      }
134
+    } else if (ch == "`") {
135
+      state.tokenize = tokenQuasi;
136
+      return tokenQuasi(stream, state);
137
+    } else if (ch == "#") {
138
+      stream.skipToEnd();
139
+      return ret("error", "error");
140
+    } else if (isOperatorChar.test(ch)) {
141
+      if (ch != ">" || !state.lexical || state.lexical.type != ">")
142
+        stream.eatWhile(isOperatorChar);
143
+      return ret("operator", "operator", stream.current());
144
+    } else if (wordRE.test(ch)) {
145
+      stream.eatWhile(wordRE);
146
+      var word = stream.current()
147
+      if (state.lastType != ".") {
148
+        if (keywords.propertyIsEnumerable(word)) {
149
+          var kw = keywords[word]
150
+          return ret(kw.type, kw.style, word)
151
+        }
152
+        if (word == "async" && stream.match(/^\s*[\(\w]/, false))
153
+          return ret("async", "keyword", word)
154
+      }
155
+      return ret("variable", "variable", word)
156
+    }
157
+  }
158
+
159
+  function tokenString(quote) {
160
+    return function(stream, state) {
161
+      var escaped = false, next;
162
+      if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
163
+        state.tokenize = tokenBase;
164
+        return ret("jsonld-keyword", "meta");
165
+      }
166
+      while ((next = stream.next()) != null) {
167
+        if (next == quote && !escaped) break;
168
+        escaped = !escaped && next == "\\";
169
+      }
170
+      if (!escaped) state.tokenize = tokenBase;
171
+      return ret("string", "string");
172
+    };
173
+  }
174
+
175
+  function tokenComment(stream, state) {
176
+    var maybeEnd = false, ch;
177
+    while (ch = stream.next()) {
178
+      if (ch == "/" && maybeEnd) {
179
+        state.tokenize = tokenBase;
180
+        break;
181
+      }
182
+      maybeEnd = (ch == "*");
183
+    }
184
+    return ret("comment", "comment");
185
+  }
186
+
187
+  function tokenQuasi(stream, state) {
188
+    var escaped = false, next;
189
+    while ((next = stream.next()) != null) {
190
+      if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
191
+        state.tokenize = tokenBase;
192
+        break;
193
+      }
194
+      escaped = !escaped && next == "\\";
195
+    }
196
+    return ret("quasi", "string-2", stream.current());
197
+  }
198
+
199
+  var brackets = "([{}])";
200
+  // This is a crude lookahead trick to try and notice that we're
201
+  // parsing the argument patterns for a fat-arrow function before we
202
+  // actually hit the arrow token. It only works if the arrow is on
203
+  // the same line as the arguments and there's no strange noise
204
+  // (comments) in between. Fallback is to only notice when we hit the
205
+  // arrow, and not declare the arguments as locals for the arrow
206
+  // body.
207
+  function findFatArrow(stream, state) {
208
+    if (state.fatArrowAt) state.fatArrowAt = null;
209
+    var arrow = stream.string.indexOf("=>", stream.start);
210
+    if (arrow < 0) return;
211
+
212
+    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
213
+      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
214
+      if (m) arrow = m.index
215
+    }
216
+
217
+    var depth = 0, sawSomething = false;
218
+    for (var pos = arrow - 1; pos >= 0; --pos) {
219
+      var ch = stream.string.charAt(pos);
220
+      var bracket = brackets.indexOf(ch);
221
+      if (bracket >= 0 && bracket < 3) {
222
+        if (!depth) { ++pos; break; }
223
+        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
224
+      } else if (bracket >= 3 && bracket < 6) {
225
+        ++depth;
226
+      } else if (wordRE.test(ch)) {
227
+        sawSomething = true;
228
+      } else if (/["'\/]/.test(ch)) {
229
+        return;
230
+      } else if (sawSomething && !depth) {
231
+        ++pos;
232
+        break;
233
+      }
234
+    }
235
+    if (sawSomething && !depth) state.fatArrowAt = pos;
236
+  }
237
+
238
+  // Parser
239
+
240
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
241
+
242
+  function JSLexical(indented, column, type, align, prev, info) {
243
+    this.indented = indented;
244
+    this.column = column;
245
+    this.type = type;
246
+    this.prev = prev;
247
+    this.info = info;
248
+    if (align != null) this.align = align;
249
+  }
250
+
251
+  function inScope(state, varname) {
252
+    for (var v = state.localVars; v; v = v.next)
253
+      if (v.name == varname) return true;
254
+    for (var cx = state.context; cx; cx = cx.prev) {
255
+      for (var v = cx.vars; v; v = v.next)
256
+        if (v.name == varname) return true;
257
+    }
258
+  }
259
+
260
+  function parseJS(state, style, type, content, stream) {
261
+    var cc = state.cc;
262
+    // Communicate our context to the combinators.
263
+    // (Less wasteful than consing up a hundred closures on every call.)
264
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
265
+
266
+    if (!state.lexical.hasOwnProperty("align"))
267
+      state.lexical.align = true;
268
+
269
+    while(true) {
270
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
271
+      if (combinator(type, content)) {
272
+        while(cc.length && cc[cc.length - 1].lex)
273
+          cc.pop()();
274
+        if (cx.marked) return cx.marked;
275
+        if (type == "variable" && inScope(state, content)) return "variable-2";
276
+        return style;
277
+      }
278
+    }
279
+  }
280
+
281
+  // Combinator utils
282
+
283
+  var cx = {state: null, column: null, marked: null, cc: null};
284
+  function pass() {
285
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
286
+  }
287
+  function cont() {
288
+    pass.apply(null, arguments);
289
+    return true;
290
+  }
291
+  function register(varname) {
292
+    function inList(list) {
293
+      for (var v = list; v; v = v.next)
294
+        if (v.name == varname) return true;
295
+      return false;
296
+    }
297
+    var state = cx.state;
298
+    cx.marked = "def";
299
+    if (state.context) {
300
+      if (inList(state.localVars)) return;
301
+      state.localVars = {name: varname, next: state.localVars};
302
+    } else {
303
+      if (inList(state.globalVars)) return;
304
+      if (parserConfig.globalVars)
305
+        state.globalVars = {name: varname, next: state.globalVars};
306
+    }
307
+  }
308
+
309
+  // Combinators
310
+
311
+  var defaultVars = {name: "this", next: {name: "arguments"}};
312
+  function pushcontext() {
313
+    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
314
+    cx.state.localVars = defaultVars;
315
+  }
316
+  function popcontext() {
317
+    cx.state.localVars = cx.state.context.vars;
318
+    cx.state.context = cx.state.context.prev;
319
+  }
320
+  function pushlex(type, info) {
321
+    var result = function() {
322
+      var state = cx.state, indent = state.indented;
323
+      if (state.lexical.type == "stat") indent = state.lexical.indented;
324
+      else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
325
+        indent = outer.indented;
326
+      state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
327
+    };
328
+    result.lex = true;
329
+    return result;
330
+  }
331
+  function poplex() {
332
+    var state = cx.state;
333
+    if (state.lexical.prev) {
334
+      if (state.lexical.type == ")")
335
+        state.indented = state.lexical.indented;
336
+      state.lexical = state.lexical.prev;
337
+    }
338
+  }
339
+  poplex.lex = true;
340
+
341
+  function expect(wanted) {
342
+    function exp(type) {
343
+      if (type == wanted) return cont();
344
+      else if (wanted == ";") return pass();
345
+      else return cont(exp);
346
+    };
347
+    return exp;
348
+  }
349
+
350
+  function statement(type, value) {
351
+    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
352
+    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
353
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
354
+    if (type == "{") return cont(pushlex("}"), block, poplex);
355
+    if (type == ";") return cont();
356
+    if (type == "if") {
357
+      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
358
+        cx.state.cc.pop()();
359
+      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
360
+    }
361
+    if (type == "function") return cont(functiondef);
362
+    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
363
+    if (type == "variable") {
364
+      if (isTS && value == "type") {
365
+        cx.marked = "keyword"
366
+        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
367
+      } if (isTS && value == "declare") {
368
+        cx.marked = "keyword"
369
+        return cont(statement)
370
+      } else {
371
+        return cont(pushlex("stat"), maybelabel);
372
+      }
373
+    }
374
+    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
375
+                                      block, poplex, poplex);
376
+    if (type == "case") return cont(expression, expect(":"));
377
+    if (type == "default") return cont(expect(":"));
378
+    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
379
+                                     statement, poplex, popcontext);
380
+    if (type == "class") return cont(pushlex("form"), className, poplex);
381
+    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
382
+    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
383
+    if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
384
+    if (type == "async") return cont(statement)
385
+    if (value == "@") return cont(expression, statement)
386
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
387
+  }
388
+  function expression(type) {
389
+    return expressionInner(type, false);
390
+  }
391
+  function expressionNoComma(type) {
392
+    return expressionInner(type, true);
393
+  }
394
+  function parenExpr(type) {
395
+    if (type != "(") return pass()
396
+    return cont(pushlex(")"), expression, expect(")"), poplex)
397
+  }
398
+  function expressionInner(type, noComma) {
399
+    if (cx.state.fatArrowAt == cx.stream.start) {
400
+      var body = noComma ? arrowBodyNoComma : arrowBody;
401
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
402
+      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
403
+    }
404
+
405
+    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
406
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
407
+    if (type == "function") return cont(functiondef, maybeop);
408
+    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
409
+    if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
410
+    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
411
+    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
412
+    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
413
+    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
414
+    if (type == "quasi") return pass(quasi, maybeop);
415
+    if (type == "new") return cont(maybeTarget(noComma));
416
+    return cont();
417
+  }
418
+  function maybeexpression(type) {
419
+    if (type.match(/[;\}\)\],]/)) return pass();
420
+    return pass(expression);
421
+  }
422
+  function maybeexpressionNoComma(type) {
423
+    if (type.match(/[;\}\)\],]/)) return pass();
424
+    return pass(expressionNoComma);
425
+  }
426
+
427
+  function maybeoperatorComma(type, value) {
428
+    if (type == ",") return cont(expression);
429
+    return maybeoperatorNoComma(type, value, false);
430
+  }
431
+  function maybeoperatorNoComma(type, value, noComma) {
432
+    var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
433
+    var expr = noComma == false ? expression : expressionNoComma;
434
+    if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
435
+    if (type == "operator") {
436
+      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
437
+      if (value == "?") return cont(expression, expect(":"), expr);
438
+      return cont(expr);
439
+    }
440
+    if (type == "quasi") { return pass(quasi, me); }
441
+    if (type == ";") return;
442
+    if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
443
+    if (type == ".") return cont(property, me);
444
+    if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
445
+    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
446
+  }
447
+  function quasi(type, value) {
448
+    if (type != "quasi") return pass();
449
+    if (value.slice(value.length - 2) != "${") return cont(quasi);
450
+    return cont(expression, continueQuasi);
451
+  }
452
+  function continueQuasi(type) {
453
+    if (type == "}") {
454
+      cx.marked = "string-2";
455
+      cx.state.tokenize = tokenQuasi;
456
+      return cont(quasi);
457
+    }
458
+  }
459
+  function arrowBody(type) {
460
+    findFatArrow(cx.stream, cx.state);
461
+    return pass(type == "{" ? statement : expression);
462
+  }
463
+  function arrowBodyNoComma(type) {
464
+    findFatArrow(cx.stream, cx.state);
465
+    return pass(type == "{" ? statement : expressionNoComma);
466
+  }
467
+  function maybeTarget(noComma) {
468
+    return function(type) {
469
+      if (type == ".") return cont(noComma ? targetNoComma : target);
470
+      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
471
+      else return pass(noComma ? expressionNoComma : expression);
472
+    };
473
+  }
474
+  function target(_, value) {
475
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
476
+  }
477
+  function targetNoComma(_, value) {
478
+    if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
479
+  }
480
+  function maybelabel(type) {
481
+    if (type == ":") return cont(poplex, statement);
482
+    return pass(maybeoperatorComma, expect(";"), poplex);
483
+  }
484
+  function property(type) {
485
+    if (type == "variable") {cx.marked = "property"; return cont();}
486
+  }
487
+  function objprop(type, value) {
488
+    if (type == "async") {
489
+      cx.marked = "property";
490
+      return cont(objprop);
491
+    } else if (type == "variable" || cx.style == "keyword") {
492
+      cx.marked = "property";
493
+      if (value == "get" || value == "set") return cont(getterSetter);
494
+      return cont(afterprop);
495
+    } else if (type == "number" || type == "string") {
496
+      cx.marked = jsonldMode ? "property" : (cx.style + " property");
497
+      return cont(afterprop);
498
+    } else if (type == "jsonld-keyword") {
499
+      return cont(afterprop);
500
+    } else if (type == "modifier") {
501
+      return cont(objprop)
502
+    } else if (type == "[") {
503
+      return cont(expression, expect("]"), afterprop);
504
+    } else if (type == "spread") {
505
+      return cont(expression, afterprop);
506
+    } else if (type == ":") {
507
+      return pass(afterprop)
508
+    }
509
+  }
510
+  function getterSetter(type) {
511
+    if (type != "variable") return pass(afterprop);
512
+    cx.marked = "property";
513
+    return cont(functiondef);
514
+  }
515
+  function afterprop(type) {
516
+    if (type == ":") return cont(expressionNoComma);
517
+    if (type == "(") return pass(functiondef);
518
+  }
519
+  function commasep(what, end, sep) {
520
+    function proceed(type, value) {
521
+      if (sep ? sep.indexOf(type) > -1 : type == ",") {
522
+        var lex = cx.state.lexical;
523
+        if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
524
+        return cont(function(type, value) {
525
+          if (type == end || value == end) return pass()
526
+          return pass(what)
527
+        }, proceed);
528
+      }
529
+      if (type == end || value == end) return cont();
530
+      return cont(expect(end));
531
+    }
532
+    return function(type, value) {
533
+      if (type == end || value == end) return cont();
534
+      return pass(what, proceed);
535
+    };
536
+  }
537
+  function contCommasep(what, end, info) {
538
+    for (var i = 3; i < arguments.length; i++)
539
+      cx.cc.push(arguments[i]);
540
+    return cont(pushlex(end, info), commasep(what, end), poplex);
541
+  }
542
+  function block(type) {
543
+    if (type == "}") return cont();
544
+    return pass(statement, block);
545
+  }
546
+  function maybetype(type, value) {
547
+    if (isTS) {
548
+      if (type == ":") return cont(typeexpr);
549
+      if (value == "?") return cont(maybetype);
550
+    }
551
+  }
552
+  function typeexpr(type, value) {
553
+    if (type == "variable") {
554
+      if (value == "keyof") {
555
+        cx.marked = "keyword"
556
+        return cont(typeexpr)
557
+      } else {
558
+        cx.marked = "type"
559
+        return cont(afterType)
560
+      }
561
+    }
562
+    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
563
+    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
564
+    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
565
+    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
566
+  }
567
+  function maybeReturnType(type) {
568
+    if (type == "=>") return cont(typeexpr)
569
+  }
570
+  function typeprop(type, value) {
571
+    if (type == "variable" || cx.style == "keyword") {
572
+      cx.marked = "property"
573
+      return cont(typeprop)
574
+    } else if (value == "?") {
575
+      return cont(typeprop)
576
+    } else if (type == ":") {
577
+      return cont(typeexpr)
578
+    } else if (type == "[") {
579
+      return cont(expression, maybetype, expect("]"), typeprop)
580
+    }
581
+  }
582
+  function typearg(type) {
583
+    if (type == "variable") return cont(typearg)
584
+    else if (type == ":") return cont(typeexpr)
585
+  }
586
+  function afterType(type, value) {
587
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
588
+    if (value == "|" || type == ".") return cont(typeexpr)
589
+    if (type == "[") return cont(expect("]"), afterType)
590
+    if (value == "extends") return cont(typeexpr)
591
+  }
592
+  function maybeTypeArgs(_, value) {
593
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
594
+  }
595
+  function vardef() {
596
+    return pass(pattern, maybetype, maybeAssign, vardefCont);
597
+  }
598
+  function pattern(type, value) {
599
+    if (type == "modifier") return cont(pattern)
600
+    if (type == "variable") { register(value); return cont(); }
601
+    if (type == "spread") return cont(pattern);
602
+    if (type == "[") return contCommasep(pattern, "]");
603
+    if (type == "{") return contCommasep(proppattern, "}");
604
+  }
605
+  function proppattern(type, value) {
606
+    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
607
+      register(value);
608
+      return cont(maybeAssign);
609
+    }
610
+    if (type == "variable") cx.marked = "property";
611
+    if (type == "spread") return cont(pattern);
612
+    if (type == "}") return pass();
613
+    return cont(expect(":"), pattern, maybeAssign);
614
+  }
615
+  function maybeAssign(_type, value) {
616
+    if (value == "=") return cont(expressionNoComma);
617
+  }
618
+  function vardefCont(type) {
619
+    if (type == ",") return cont(vardef);
620
+  }
621
+  function maybeelse(type, value) {
622
+    if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
623
+  }
624
+  function forspec(type) {
625
+    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
626
+  }
627
+  function forspec1(type) {
628
+    if (type == "var") return cont(vardef, expect(";"), forspec2);
629
+    if (type == ";") return cont(forspec2);
630
+    if (type == "variable") return cont(formaybeinof);
631
+    return pass(expression, expect(";"), forspec2);
632
+  }
633
+  function formaybeinof(_type, value) {
634
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
635
+    return cont(maybeoperatorComma, forspec2);
636
+  }
637
+  function forspec2(type, value) {
638
+    if (type == ";") return cont(forspec3);
639
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
640
+    return pass(expression, expect(";"), forspec3);
641
+  }
642
+  function forspec3(type) {
643
+    if (type != ")") cont(expression);
644
+  }
645
+  function functiondef(type, value) {
646
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
647
+    if (type == "variable") {register(value); return cont(functiondef);}
648
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
649
+    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
650
+  }
651
+  function funarg(type) {
652
+    if (type == "spread" || type == "modifier") return cont(funarg);
653
+    return pass(pattern, maybetype, maybeAssign);
654
+  }
655
+  function classExpression(type, value) {
656
+    // Class expressions may have an optional name.
657
+    if (type == "variable") return className(type, value);
658
+    return classNameAfter(type, value);
659
+  }
660
+  function className(type, value) {
661
+    if (type == "variable") {register(value); return cont(classNameAfter);}
662
+  }
663
+  function classNameAfter(type, value) {
664
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
665
+    if (value == "extends" || value == "implements" || (isTS && type == ","))
666
+      return cont(isTS ? typeexpr : expression, classNameAfter);
667
+    if (type == "{") return cont(pushlex("}"), classBody, poplex);
668
+  }
669
+  function classBody(type, value) {
670
+    if (type == "modifier" || type == "async" ||
671
+        (type == "variable" &&
672
+         (value == "static" || value == "get" || value == "set") &&
673
+         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
674
+      cx.marked = "keyword";
675
+      return cont(classBody);
676
+    }
677
+    if (type == "variable" || cx.style == "keyword") {
678
+      cx.marked = "property";
679
+      return cont(isTS ? classfield : functiondef, classBody);
680
+    }
681
+    if (type == "[")
682
+      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
683
+    if (value == "*") {
684
+      cx.marked = "keyword";
685
+      return cont(classBody);
686
+    }
687
+    if (type == ";") return cont(classBody);
688
+    if (type == "}") return cont();
689
+    if (value == "@") return cont(expression, classBody)
690
+  }
691
+  function classfield(type, value) {
692
+    if (value == "?") return cont(classfield)
693
+    if (type == ":") return cont(typeexpr, maybeAssign)
694
+    if (value == "=") return cont(expressionNoComma)
695
+    return pass(functiondef)
696
+  }
697
+  function afterExport(type, value) {
698
+    if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
699
+    if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
700
+    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
701
+    return pass(statement);
702
+  }
703
+  function exportField(type, value) {
704
+    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
705
+    if (type == "variable") return pass(expressionNoComma, exportField);
706
+  }
707
+  function afterImport(type) {
708
+    if (type == "string") return cont();
709
+    return pass(importSpec, maybeMoreImports, maybeFrom);
710
+  }
711
+  function importSpec(type, value) {
712
+    if (type == "{") return contCommasep(importSpec, "}");
713
+    if (type == "variable") register(value);
714
+    if (value == "*") cx.marked = "keyword";
715
+    return cont(maybeAs);
716
+  }
717
+  function maybeMoreImports(type) {
718
+    if (type == ",") return cont(importSpec, maybeMoreImports)
719
+  }
720
+  function maybeAs(_type, value) {
721
+    if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
722
+  }
723
+  function maybeFrom(_type, value) {
724
+    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
725
+  }
726
+  function arrayLiteral(type) {
727
+    if (type == "]") return cont();
728
+    return pass(commasep(expressionNoComma, "]"));
729
+  }
730
+
731
+  function isContinuedStatement(state, textAfter) {
732
+    return state.lastType == "operator" || state.lastType == "," ||
733
+      isOperatorChar.test(textAfter.charAt(0)) ||
734
+      /[,.]/.test(textAfter.charAt(0));
735
+  }
736
+
737
+  function expressionAllowed(stream, state, backUp) {
738
+    return state.tokenize == tokenBase &&
739
+      /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
740
+      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
741
+  }
742
+
743
+  // Interface
744
+
745
+  return {
746
+    startState: function(basecolumn) {
747
+      var state = {
748
+        tokenize: tokenBase,
749
+        lastType: "sof",
750
+        cc: [],
751
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
752
+        localVars: parserConfig.localVars,
753
+        context: parserConfig.localVars && {vars: parserConfig.localVars},
754
+        indented: basecolumn || 0
755
+      };
756
+      if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
757
+        state.globalVars = parserConfig.globalVars;
758
+      return state;
759
+    },
760
+
761
+    token: function(stream, state) {
762
+      if (stream.sol()) {
763
+        if (!state.lexical.hasOwnProperty("align"))
764
+          state.lexical.align = false;
765
+        state.indented = stream.indentation();
766
+        findFatArrow(stream, state);
767
+      }
768
+      if (state.tokenize != tokenComment && stream.eatSpace()) return null;
769
+      var style = state.tokenize(stream, state);
770
+      if (type == "comment") return style;
771
+      state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
772
+      return parseJS(state, style, type, content, stream);
773
+    },
774
+
775
+    indent: function(state, textAfter) {
776
+      if (state.tokenize == tokenComment) return CodeMirror.Pass;
777
+      if (state.tokenize != tokenBase) return 0;
778
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
779
+      // Kludge to prevent 'maybelse' from blocking lexical scope pops
780
+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
781
+        var c = state.cc[i];
782
+        if (c == poplex) lexical = lexical.prev;
783
+        else if (c != maybeelse) break;
784
+      }
785
+      while ((lexical.type == "stat" || lexical.type == "form") &&
786
+             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
787
+                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
788
+                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
789
+        lexical = lexical.prev;
790
+      if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
791
+        lexical = lexical.prev;
792
+      var type = lexical.type, closing = firstChar == type;
793
+
794
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
795
+      else if (type == "form" && firstChar == "{") return lexical.indented;
796
+      else if (type == "form") return lexical.indented + indentUnit;
797
+      else if (type == "stat")
798
+        return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
799
+      else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
800
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
801
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
802
+      else return lexical.indented + (closing ? 0 : indentUnit);
803
+    },
804
+
805
+    electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
806
+    blockCommentStart: jsonMode ? null : "/*",
807
+    blockCommentEnd: jsonMode ? null : "*/",
808
+    lineComment: jsonMode ? null : "//",
809
+    fold: "brace",
810
+    closeBrackets: "()[]{}''\"\"``",
811
+
812
+    helperType: jsonMode ? "json" : "javascript",
813
+    jsonldMode: jsonldMode,
814
+    jsonMode: jsonMode,
815
+
816
+    expressionAllowed: expressionAllowed,
817
+
818
+    skipExpression: function(state) {
819
+      var top = state.cc[state.cc.length - 1]
820
+      if (top == expression || top == expressionNoComma) state.cc.pop()
821
+    }
822
+  };
823
+});
824
+
825
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
826
+
827
+CodeMirror.defineMIME("text/javascript", "javascript");
828
+CodeMirror.defineMIME("text/ecmascript", "javascript");
829
+CodeMirror.defineMIME("application/javascript", "javascript");
830
+CodeMirror.defineMIME("application/x-javascript", "javascript");
831
+CodeMirror.defineMIME("application/ecmascript", "javascript");
832
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
833
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
834
+CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
835
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
836
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
837
+
838
+});

+ 2 - 1
xxl-job-core/src/main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java 파일 보기

@@ -120,7 +120,8 @@ public class ExecutorBizImpl implements ExecutorBiz {
120 120
                 }
121 121
             }
122 122
         } else if (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(triggerParam.getGlueType())
123
-                || GlueTypeEnum.GLUE_PYTHON==GlueTypeEnum.match(triggerParam.getGlueType()) ) {
123
+                || GlueTypeEnum.GLUE_PYTHON==GlueTypeEnum.match(triggerParam.getGlueType())
124
+                || GlueTypeEnum.GLUE_NODEJS==GlueTypeEnum.match(triggerParam.getGlueType())) {
124 125
 
125 126
             // valid old jobThread
126 127
             if (jobThread != null &&

+ 2 - 1
xxl-job-core/src/main/java/com/xxl/job/core/glue/GlueTypeEnum.java 파일 보기

@@ -8,7 +8,8 @@ public enum GlueTypeEnum {
8 8
     BEAN("BEAN模式"),
9 9
     GLUE_GROOVY("GLUE模式(Java)"),
10 10
     GLUE_SHELL("GLUE模式(Shell)"),
11
-    GLUE_PYTHON("GLUE模式(Python)");
11
+    GLUE_PYTHON("GLUE模式(Python)"),
12
+    GLUE_NODEJS("GLUE模式(Nodejs)");
12 13
 
13 14
     private String desc;
14 15
     private GlueTypeEnum(String desc) {

+ 3 - 0
xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java 파일 보기

@@ -40,6 +40,9 @@ public class ScriptJobHandler extends IJobHandler {
40 40
         } else if (GlueTypeEnum.GLUE_PYTHON == glueType) {
41 41
             cmd = "python";
42 42
             scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".py");
43
+        } else if (GlueTypeEnum.GLUE_NODEJS == glueType) {
44
+            cmd = "node";
45
+            scriptFileName = XxlJobFileAppender.logPath.concat("gluesource/").concat(String.valueOf(jobId)).concat("_").concat(String.valueOf(glueUpdatetime)).concat(".js");
43 46
         }
44 47
 
45 48
         // make script file