@onify/flow-extensions 3.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ # 4.1.0
5
+
6
+ - Support sequence flow properties and take execution listener. Requires `bpmn-elements >= 9.2`
7
+
8
+ # 4.0.0
9
+
10
+ - make sure sub-process extensions are only triggered once. Sub process activities unintentionally trigger formatting since events bubble
11
+ - add script element type to registered scripts
12
+ - restructure and split up index.js to separate files
13
+ - abide to new lint rules
14
+
4
15
  # 3.0.0
5
16
 
6
17
  - Support activity script and expression execution listeners
package/README.md CHANGED
@@ -120,3 +120,92 @@ function getModdleContext(source, options) {
120
120
  return bpmnModdle.fromXML(Buffer.isBuffer(source) ? source.toString() : source.trim());
121
121
  }
122
122
  ```
123
+
124
+ ## Extend sequence flow with properties and take listeners
125
+
126
+ ```js
127
+ const {OnifySequenceFlow, extensions} = require('@onify/flow-extensions');
128
+ const FlowScripts = require('@onify/flow-extensions/dist/src/FlowScripts');
129
+ const {Engine} = require('bpmn-engine');
130
+ const Elements = require('bpmn-elements');
131
+
132
+ const source = `
133
+ <?xml version="1.0" encoding="UTF-8"?>
134
+ <definitions
135
+ id="Def_0"
136
+ xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
137
+ xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
138
+ targetNamespace="http://bpmn.io/schema/bpmn">
139
+ <process id="Process_1kk79yr" isExecutable="true">
140
+ <startEvent id="start" />
141
+ <sequenceFlow id="to-script" sourceRef="start" targetRef="script">
142
+ <extensionElements>
143
+ <camunda:properties>
144
+ <camunda:property name="source" value="\${content.id}" />
145
+ </camunda:properties>
146
+ <camunda:executionListener event="take">
147
+ <camunda:script scriptFormat="js">environment.output.fields = listener.fields; next();</camunda:script>
148
+ <camunda:field name="taken">
149
+ <camunda:expression>\${true}</camunda:expression>
150
+ </camunda:field>
151
+ <camunda:field name="bar">
152
+ <camunda:string>baz</camunda:string>
153
+ </camunda:field>
154
+ </camunda:executionListener>
155
+ </extensionElements>
156
+ </sequenceFlow>
157
+ <sequenceFlow id="to-end" sourceRef="script" targetRef="end">
158
+ <extensionElements>
159
+ <camunda:properties>
160
+ <camunda:property name="foo" value="bar" />
161
+ </camunda:properties>
162
+ </extensionElements>
163
+ </sequenceFlow>
164
+ <scriptTask id="script" name="script" scriptFormat="js">
165
+ <script>next(null, { foo: environment.variables.required.input });</script>
166
+ </scriptTask>
167
+ <boundaryEvent id="catch-err" attachedToRef="script">
168
+ <errorEventDefinition />
169
+ </boundaryEvent>
170
+ <endEvent id="end-err">
171
+ <extensionElements>
172
+ <camunda:executionListener event="start">
173
+ <camunda:script scriptFormat="js">
174
+ environment.output.failedBy = content.inbound[0].properties.error;
175
+ if (next) next();
176
+ </camunda:script>
177
+ </camunda:executionListener>
178
+ </extensionElements>
179
+ </endEvent>
180
+ <sequenceFlow id="to-end-err" sourceRef="catch-err" targetRef="end-err">
181
+ <extensionElements>
182
+ <camunda:properties>
183
+ <camunda:property name="error" value="\${content.output}" />
184
+ </camunda:properties>
185
+ </extensionElements>
186
+ </sequenceFlow>
187
+ <endEvent id="end" />
188
+ </process>
189
+ </definitions>`;
190
+
191
+ const engine = new Engine({
192
+ name: 'sequence flow extension',
193
+ source,
194
+ moddleOptions: {
195
+ camunda: require('camunda-bpmn-moddle/resources/camunda.json'),
196
+ },
197
+ extensions: {
198
+ onify: extensions,
199
+ },
200
+ scripts: new FlowScripts('sequence flow extension'),
201
+ elements: {
202
+ ...Elements,
203
+ SequenceFlow: OnifySequenceFlow,
204
+ },
205
+ });
206
+
207
+ engine.execute((err, instance) => {
208
+ if (err) throw err;
209
+ console.log(instance.name, instance.environment.output);
210
+ });
211
+ ```
package/dist/index.js CHANGED
@@ -3,423 +3,20 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "OnifySequenceFlow", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _OnifySequenceFlow.OnifySequenceFlow;
10
+ }
11
+ });
6
12
  exports.extendFn = extendFn;
7
13
  exports.extensions = extensions;
8
- var _cronParser = _interopRequireDefault(require("cron-parser"));
9
- var _IOProperties = _interopRequireDefault(require("./src/IOProperties.js"));
10
- var _IOForm = _interopRequireDefault(require("./src/IOForm.js"));
11
- var _Connector = _interopRequireDefault(require("./src/Connector.js"));
12
- var _ServiceExpression = _interopRequireDefault(require("./src/ServiceExpression.js"));
13
- var _IO = require("./src/IO.js");
14
- var _ExecutionListeners = _interopRequireDefault(require("./src/ExecutionListeners.js"));
15
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
- const iso8601cycle = /^\s*(R\d+\/)?P\w+/i;
17
- class FormatError extends Error {
18
- constructor(elementId, err) {
19
- super(`<${elementId}> ${err.message}`);
20
- this.code = 'EFLOW_FORMAT';
21
- this.output = {
22
- statusCode: 500
23
- };
24
- }
25
- }
14
+ var _OnifyProcessExtensions = require("./src/OnifyProcessExtensions.js");
15
+ var _OnifyElementExtensions = require("./src/OnifyElementExtensions.js");
16
+ var _OnifySequenceFlow = require("./src/OnifySequenceFlow.js");
26
17
  function extensions(element, context) {
27
- if (element.type === 'bpmn:Process') return new OnifyProcessExtensions(element, context);
28
- return new OnifyElementExtensions(element, context);
29
- }
30
- class OnifyProcessExtensions {
31
- constructor(bp, context) {
32
- this.process = bp;
33
- this.context = context;
34
- this.extensions = getExtensions(bp, context);
35
- this._activate();
36
- }
37
- _activate() {
38
- const bp = this.process;
39
- bp.on('process.enter', elementApi => {
40
- try {
41
- const result = this._onEnter(elementApi);
42
- const environment = elementApi.environment;
43
- environment.assignVariables(result);
44
- } catch (err) {
45
- elementApi.broker.publish('event', 'process.error', {
46
- ...elementApi.content,
47
- error: new FormatError(bp.id, err)
48
- }, {
49
- mandatory: true,
50
- type: 'error'
51
- });
52
- }
53
- }, {
54
- consumerTag: '_onify-extension-on-enter'
55
- });
56
- }
57
- _onEnter(elementApi) {
58
- const {
59
- format,
60
- properties
61
- } = this.extensions;
62
- const result = {};
63
- Object.assign(result, format.resolve(elementApi));
64
- Object.assign(elementApi.content, result);
65
- if (properties) {
66
- Object.assign(result, properties.resolve(elementApi));
67
- Object.assign(elementApi.content, result);
68
- }
69
- return result;
70
- }
71
- }
72
- class OnifyElementExtensions {
73
- constructor(activity, context) {
74
- this.activity = activity;
75
- this.context = context;
76
- const {
77
- Service
78
- } = this.extensions = getExtensions(activity, context);
79
- if (Service) {
80
- activity.behaviour.Service = Service;
81
- }
82
- }
83
- activate(message) {
84
- const activity = this.activity;
85
- const broker = activity.broker;
86
- const formatQ = activity.broker.getQueue('format-run-q');
87
- if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
88
- activity.on('start', elementApi => {
89
- this._formatOnEnter(broker, formatQ, elementApi);
90
- }, {
91
- consumerTag: '_onify-extension-on-enter'
92
- });
93
- } else {
94
- activity.on('enter', elementApi => {
95
- this._formatOnEnter(broker, formatQ, elementApi);
96
- }, {
97
- consumerTag: '_onify-extension-on-enter'
98
- });
99
- }
100
- activity.on('activity.execution.completed', async elementApi => {
101
- formatQ.queueMessage({
102
- routingKey: 'run.end.format'
103
- }, {
104
- endRoutingKey: 'run.end.complete'
105
- }, {
106
- persistent: false
107
- });
108
- try {
109
- var format = await this._onExecuted(elementApi);
110
- } catch (err) {
111
- return broker.publish('format', 'run.end.error', {
112
- error: err
113
- }, {
114
- persistent: false
115
- });
116
- }
117
- broker.publish('format', 'run.end.complete', {
118
- ...format
119
- }, {
120
- persistent: false
121
- });
122
- }, {
123
- consumerTag: '_onify-extension-on-executed'
124
- });
125
- const executionListeners = this.extensions.listeners;
126
- if (executionListeners !== null && executionListeners !== void 0 && executionListeners.onStart) {
127
- activity.on('start', async elementApi => {
128
- formatQ.queueMessage({
129
- routingKey: 'run.listener.start'
130
- }, {
131
- endRoutingKey: 'run.listener.start.complete'
132
- }, {
133
- persistent: false
134
- });
135
- try {
136
- var format = await executionListeners.execute('start', elementApi);
137
- } catch (err) {
138
- return broker.publish('format', 'run.listener.start.error', {
139
- error: err
140
- }, {
141
- persistent: false
142
- });
143
- }
144
- broker.publish('format', 'run.listener.start.complete', {
145
- ...format
146
- }, {
147
- persistent: false
148
- });
149
- }, {
150
- consumerTag: '_onify-extension-on-listenerstart'
151
- });
152
- }
153
- if (executionListeners !== null && executionListeners !== void 0 && executionListeners.onEnd) {
154
- activity.on('end', async elementApi => {
155
- formatQ.queueMessage({
156
- routingKey: 'run.listener.end'
157
- }, {
158
- endRoutingKey: 'run.listener.end.complete'
159
- }, {
160
- persistent: false
161
- });
162
- try {
163
- var format = await executionListeners.execute('end', elementApi);
164
- } catch (err) {
165
- return broker.publish('format', 'run.listener.end.error', {
166
- error: err
167
- }, {
168
- persistent: false
169
- });
170
- }
171
- broker.publish('format', 'run.listener.end.complete', {
172
- ...format
173
- }, {
174
- persistent: false
175
- });
176
- }, {
177
- consumerTag: '_onify-extension-on-listenerend'
178
- });
179
- }
180
- }
181
- deactivate() {
182
- const broker = this.activity.broker;
183
- broker.cancel('_onify-extension-on-enter');
184
- broker.cancel('_onify-extension-on-executed');
185
- broker.cancel('_onify-extension-on-listenerstart');
186
- broker.cancel('_onify-extension-on-listenerend');
187
- }
188
- async _formatOnEnter(broker, formatQ, elementApi) {
189
- formatQ.queueMessage({
190
- routingKey: 'run.enter.format'
191
- }, {
192
- endRoutingKey: 'run.enter.complete'
193
- }, {
194
- persistent: false
195
- });
196
- try {
197
- var format = await this._onEnter(elementApi);
198
- } catch (err) {
199
- return broker.publish('format', 'run.enter.error', {
200
- error: err
201
- }, {
202
- persistent: false
203
- });
204
- }
205
- broker.publish('format', 'run.enter.complete', format, {
206
- persistent: false
207
- });
208
- }
209
- async _onEnter(elementApi) {
210
- const {
211
- format,
212
- properties,
213
- io,
214
- form
215
- } = this.extensions;
216
- const result = {};
217
- Object.assign(result, format.resolve(elementApi));
218
- Object.assign(elementApi.content, result);
219
- if (properties) {
220
- Object.assign(result, {
221
- properties: properties.resolve(elementApi)
222
- });
223
- Object.assign(elementApi.content, result);
224
- }
225
- if (io) {
226
- var _io$input, _io$output;
227
- if ((_io$input = io.input) !== null && _io$input !== void 0 && _io$input.length) {
228
- const input = await io.getInput(this.activity, elementApi);
229
- Object.assign(result, {
230
- input
231
- });
232
- Object.assign(elementApi.content, result);
233
- }
234
- if ((_io$output = io.output) !== null && _io$output !== void 0 && _io$output.length) {
235
- result.assignToOutput = true;
236
- }
237
- }
238
- if (form) {
239
- Object.assign(result, {
240
- form: form.resolve(elementApi)
241
- });
242
- }
243
- return result;
244
- }
245
- async _onExecuted(elementApi) {
246
- const {
247
- properties,
248
- io
249
- } = this.extensions;
250
- const result = {};
251
- if (io !== null && io !== void 0 && io.output.length) {
252
- const output = await io.getOutput(this.activity, elementApi);
253
- Object.assign(result, {
254
- output
255
- });
256
- Object.assign(elementApi.content, {
257
- output
258
- });
259
- }
260
- if (properties) {
261
- const props = properties.resolve(elementApi);
262
- Object.assign(result, {
263
- properties: props
264
- });
265
- Object.assign(elementApi.content, {
266
- properties: props
267
- });
268
- }
269
- const {
270
- output,
271
- assignToOutput,
272
- resultVariable
273
- } = elementApi.content;
274
- if (output === undefined || output === null) return result;
275
- if (assignToOutput && typeof output === 'object') {
276
- Object.assign(this.activity.environment.output, output);
277
- } else if (resultVariable) {
278
- elementApi.environment.output[resultVariable] = output;
279
- }
280
- return result;
281
- }
282
- }
283
- function getExtensions(element, context) {
284
- var _element$behaviour$ex, _ext$values, _ext$fields;
285
- const result = {
286
- format: element.type === 'bpmn:Process' ? new FormatProcess(element) : new FormatActivity(element)
287
- };
288
- const expression = element.behaviour.expression;
289
- if (expression) result.Service = _ServiceExpression.default;
290
- const extensions = (_element$behaviour$ex = element.behaviour.extensionElements) === null || _element$behaviour$ex === void 0 ? void 0 : _element$behaviour$ex.values;
291
- if (!extensions) return result;
292
- const listeners = new _ExecutionListeners.default(element, context);
293
- let listener = 0;
294
- for (const ext of extensions) {
295
- switch (ext.$type) {
296
- case 'camunda:Properties':
297
- if ((_ext$values = ext.values) !== null && _ext$values !== void 0 && _ext$values.length) result.properties = new _IOProperties.default(element, ext);
298
- break;
299
- case 'camunda:InputOutput':
300
- result.io = new _IO.InputOutput(element.id, ext, context);
301
- break;
302
- case 'camunda:FormData':
303
- if ((_ext$fields = ext.fields) !== null && _ext$fields !== void 0 && _ext$fields.length) result.form = new _IOForm.default(element, ext);
304
- break;
305
- case 'camunda:Connector':
306
- {
307
- const {
308
- connectorId,
309
- inputOutput
310
- } = ext;
311
- const io = inputOutput && new _IO.InputOutput(`${element.id}/${ext.$type.toLowerCase()}`, inputOutput, context);
312
- result.Service = _Connector.default.bind(_Connector.default, connectorId, io);
313
- break;
314
- }
315
- case 'camunda:ExecutionListener':
316
- listeners.add(ext, listener++);
317
- break;
318
- }
319
- }
320
- if (listeners.length) result.listeners = listeners;
321
- return result;
322
- }
323
- class FormatActivity {
324
- constructor(activity) {
325
- this.activity = activity;
326
- this.resultVariable = activity.behaviour.resultVariable;
327
- let timeCycles;
328
- if (activity.eventDefinitions) {
329
- for (const ed of activity.eventDefinitions.filter(e => e.type === 'bpmn:TimerEventDefinition')) {
330
- if (!('timeCycle' in ed)) continue;
331
- timeCycles = timeCycles || [];
332
- timeCycles.push(ed.timeCycle);
333
- }
334
- }
335
- this.timeCycles = timeCycles;
336
- }
337
- resolve(elementApi) {
338
- var _documentation$, _user, _groups;
339
- let user, groups, assigneeValue, description;
340
- const activity = this.activity;
341
- const {
342
- documentation,
343
- candidateUsers,
344
- candidateGroups,
345
- scheduledStart,
346
- assignee
347
- } = activity.behaviour;
348
- if (candidateUsers) user = resolveAndSplit(elementApi, candidateUsers);
349
- if (candidateGroups) groups = resolveAndSplit(elementApi, candidateGroups);
350
- if (assignee) assigneeValue = elementApi.resolveExpression(assignee);
351
- if (documentation) description = (_documentation$ = documentation[0]) === null || _documentation$ === void 0 ? void 0 : _documentation$.text;
352
- let expireAt;
353
- let timeCycles = this.timeCycles;
354
- if (timeCycles) {
355
- for (const cycle of timeCycles) {
356
- const cron = elementApi.resolveExpression(cycle);
357
- if (!cron || iso8601cycle.test(cron)) continue;
358
- const expireAtDt = _cronParser.default.parseExpression(cron).next().toDate();
359
- if (!expireAt || expireAtDt < expireAt) expireAt = expireAtDt;
360
- }
361
- }
362
- return {
363
- ...(this.resultVariable && {
364
- resultVariable: this.resultVariable
365
- }),
366
- ...(scheduledStart && activity.parent.type === 'bpmn:Process' && {
367
- scheduledStart
368
- }),
369
- ...(((_user = user) === null || _user === void 0 ? void 0 : _user.length) && {
370
- candidateUsers: user
371
- }),
372
- ...(((_groups = groups) === null || _groups === void 0 ? void 0 : _groups.length) && {
373
- candidateGroups: groups
374
- }),
375
- ...(!elementApi.content.description && description && {
376
- description: elementApi.resolveExpression(description)
377
- }),
378
- ...(expireAt && {
379
- expireAt
380
- }),
381
- ...(assigneeValue && {
382
- assignee: assigneeValue
383
- })
384
- };
385
- }
386
- }
387
- class FormatProcess {
388
- constructor(bp) {
389
- this.process = bp;
390
- }
391
- resolve(elementApi) {
392
- var _documentation$2, _user2, _groups2;
393
- let user, groups, description;
394
- const bp = this.process;
395
- const {
396
- documentation,
397
- candidateStarterUsers,
398
- candidateStarterGroups
399
- } = bp.behaviour;
400
- if (candidateStarterUsers) user = resolveAndSplit(elementApi, candidateStarterUsers);
401
- if (candidateStarterGroups) groups = resolveAndSplit(elementApi, candidateStarterGroups);
402
- if (documentation) description = (_documentation$2 = documentation[0]) === null || _documentation$2 === void 0 ? void 0 : _documentation$2.text;
403
- return {
404
- ...(((_user2 = user) === null || _user2 === void 0 ? void 0 : _user2.length) && {
405
- candidateStarterUsers: user
406
- }),
407
- ...(((_groups2 = groups) === null || _groups2 === void 0 ? void 0 : _groups2.length) && {
408
- candidateStarterGroups: groups
409
- }),
410
- ...(!elementApi.content.description && description && {
411
- description: elementApi.resolveExpression(description)
412
- })
413
- };
414
- }
415
- }
416
- function resolveAndSplit(elementApi, str) {
417
- if (Array.isArray(str)) return str.filter(Boolean);
418
- if (typeof str !== 'string') return;
419
- const resolved = elementApi.resolveExpression(str);
420
- if (Array.isArray(resolved)) return resolved.filter(Boolean);
421
- if (typeof resolved !== 'string') return;
422
- return resolved.split(',').map(g => g.trim && g.trim().toLowerCase()).filter(Boolean);
18
+ if (element.type === 'bpmn:Process') return new _OnifyProcessExtensions.OnifyProcessExtensions(element, context);
19
+ return new _OnifyElementExtensions.OnifyElementExtensions(element, context);
423
20
  }
424
21
  function extendFn(behaviour, context) {
425
22
  var _behaviour$extensionE;
@@ -461,10 +58,12 @@ function registerIOScripts(parentId, context, type, ioBehaviour) {
461
58
  } of inputParameters.concat(outputParameters)) {
462
59
  if (!definition) continue;
463
60
  if (definition.$type !== 'camunda:Script') continue;
464
- const filename = `${parentId}/${type}/${$type}/${name}`;
61
+ const ioType = `${type}/${$type}`;
62
+ const filename = `${parentId}/${ioType}/${name}`;
465
63
  context.addScript(filename, {
466
64
  id: filename,
467
65
  scriptFormat: definition.scriptFormat,
66
+ type: ioType,
468
67
  ...(definition.value && {
469
68
  body: definition.value
470
69
  }),
@@ -484,6 +83,7 @@ function registerListenerScript(parentId, context, type, listener, pos) {
484
83
  context.addScript(id, {
485
84
  id,
486
85
  scriptFormat: script.scriptFormat,
86
+ type,
487
87
  ...(script.value && {
488
88
  body: script.value
489
89
  }),
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = Connector;
7
- var _Errors = require("./Errors");
7
+ var _Errors = require("./Errors.js");
8
8
  function Connector(connectorId, io, activity, executionMessage) {
9
9
  if (!(this instanceof Connector)) return new Connector(connectorId, io, activity, executionMessage);
10
10
  this.connectorId = connectorId;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.NotImplemented = void 0;
6
+ exports.NotImplemented = exports.FormatError = void 0;
7
7
  class NotImplemented extends Error {
8
8
  constructor(serviceId) {
9
9
  super(`${serviceId} service function not found`);
@@ -13,4 +13,14 @@ class NotImplemented extends Error {
13
13
  };
14
14
  }
15
15
  }
16
- exports.NotImplemented = NotImplemented;
16
+ exports.NotImplemented = NotImplemented;
17
+ class FormatError extends Error {
18
+ constructor(elementId, err) {
19
+ super(`<${elementId}> ${err.message}`);
20
+ this.code = 'EFLOW_FORMAT';
21
+ this.output = {
22
+ statusCode: 500
23
+ };
24
+ }
25
+ }
26
+ exports.FormatError = FormatError;
@@ -62,6 +62,8 @@ class ScriptListener extends Listener {
62
62
  id: this.id,
63
63
  resolveExpression
64
64
  });
65
+ const listenerFields = this._getFields(environment, scope);
66
+ if (listenerFields) scope.listener.fields = listenerFields;
65
67
  const script = this.environment.scripts.getScript(this.extension.scriptFormat, {
66
68
  id: this.id
67
69
  });
@@ -109,23 +111,27 @@ class ExecutionListeners {
109
111
  this.activity = activity;
110
112
  this.context = context;
111
113
  this.listeners = [];
112
- this.onStart = false;
113
- this.onEnd = false;
114
114
  }
115
115
  get length() {
116
116
  return this.listeners.length;
117
117
  }
118
+ get onStart() {
119
+ return this.listeners.some(l => l.event === 'start');
120
+ }
121
+ get onEnd() {
122
+ return this.listeners.some(l => l.event === 'end');
123
+ }
124
+ get onTake() {
125
+ return this.listeners.some(l => l.event === 'take');
126
+ }
118
127
  add(extension, pos) {
119
128
  const {
120
- event,
121
129
  script,
122
130
  expression
123
131
  } = extension;
124
132
  if (!script && !expression) return;
125
133
  const list = this.listeners;
126
134
  if (script && (script.value || script.resource)) list.push(new ScriptListener(this.activity, this.context, extension, pos));else if (expression) list.push(new ExpressionListener(this.activity, this.context, extension));
127
- if (event === 'start') this.onStart = true;
128
- if (event === 'end') this.onEnd = true;
129
135
  }
130
136
  async execute(event, message) {
131
137
  const found = this.listeners.filter(l => l.event === event);
@@ -80,7 +80,7 @@ FlowScripts.prototype.register = function register({
80
80
  resource = behaviour.resource;
81
81
  }
82
82
  }
83
- if (!language) language = 'javascript';
83
+ if (!language) return;
84
84
  if (!['js', 'javascript'].includes(language.toLowerCase().trim())) return;
85
85
  language = 'javascript';
86
86
  const name = this._name;