@opensumi/ide-task 2.21.13 → 2.22.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.
Files changed (50) hide show
  1. package/lib/browser/index.js +2 -2
  2. package/lib/browser/index.js.map +1 -1
  3. package/lib/browser/parser.js +4 -4
  4. package/lib/browser/parser.js.map +1 -1
  5. package/lib/browser/task-config.d.ts +4 -4
  6. package/lib/browser/task-config.d.ts.map +1 -1
  7. package/lib/browser/task-config.js +31 -31
  8. package/lib/browser/task-config.js.map +1 -1
  9. package/lib/browser/task-executor.d.ts.map +1 -1
  10. package/lib/browser/task-executor.js +16 -22
  11. package/lib/browser/task-executor.js.map +1 -1
  12. package/lib/browser/task-preferences.contribution.js.map +1 -1
  13. package/lib/browser/task-preferences.provider.d.ts +2 -2
  14. package/lib/browser/task-preferences.provider.d.ts.map +1 -1
  15. package/lib/browser/task-preferences.provider.js +2 -2
  16. package/lib/browser/task-preferences.provider.js.map +1 -1
  17. package/lib/browser/task.contribution.js.map +1 -1
  18. package/lib/browser/task.schema.d.ts +2 -2
  19. package/lib/browser/task.schema.d.ts.map +1 -1
  20. package/lib/browser/task.schema.js +5 -1
  21. package/lib/browser/task.schema.js.map +1 -1
  22. package/lib/browser/task.service.d.ts +3 -1
  23. package/lib/browser/task.service.d.ts.map +1 -1
  24. package/lib/browser/task.service.js +10 -5
  25. package/lib/browser/task.service.js.map +1 -1
  26. package/lib/browser/terminal-task-system.js +11 -11
  27. package/lib/browser/terminal-task-system.js.map +1 -1
  28. package/lib/common/index.d.ts +1 -1
  29. package/lib/common/index.d.ts.map +1 -1
  30. package/lib/common/task.d.ts +4 -4
  31. package/lib/common/task.d.ts.map +1 -1
  32. package/lib/common/task.js +7 -7
  33. package/lib/common/task.js.map +1 -1
  34. package/package.json +15 -14
  35. package/src/browser/index.ts +33 -0
  36. package/src/browser/parser.ts +944 -0
  37. package/src/browser/problem-collector.ts +71 -0
  38. package/src/browser/problem-line-matcher.ts +461 -0
  39. package/src/browser/task-config.ts +2302 -0
  40. package/src/browser/task-executor.ts +296 -0
  41. package/src/browser/task-preferences.contribution.ts +9 -0
  42. package/src/browser/task-preferences.provider.ts +23 -0
  43. package/src/browser/task-preferences.ts +14 -0
  44. package/src/browser/task.contribution.ts +70 -0
  45. package/src/browser/task.schema.ts +368 -0
  46. package/src/browser/task.service.ts +504 -0
  47. package/src/browser/terminal-task-system.ts +340 -0
  48. package/src/common/index.ts +165 -0
  49. package/src/common/task.ts +1174 -0
  50. package/src/index.ts +1 -0
@@ -0,0 +1,2302 @@
1
+ /* ---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+
6
+ import {
7
+ isStringArray,
8
+ isString,
9
+ ProblemMatcherType,
10
+ NamedProblemMatcher,
11
+ objects,
12
+ formatLocalize,
13
+ isBoolean,
14
+ isArray,
15
+ ProblemMatcher,
16
+ isUndefined,
17
+ IJSONSchemaMap,
18
+ uuid,
19
+ IStringDictionary,
20
+ KeyedTaskIdentifier,
21
+ IProblemMatcherRegistry,
22
+ ITaskDefinitionRegistry,
23
+ IProblemPatternRegistry,
24
+ NamedProblemPattern,
25
+ Platform,
26
+ } from '@opensumi/ide-core-common';
27
+
28
+ import { IWorkspaceFolder } from '../common';
29
+ import * as TaskTypes from '../common/task';
30
+
31
+ import { IProblemReporterBase, ValidationStatus, ProblemMatcherParser, Config } from './parser';
32
+
33
+ const { deepClone } = objects;
34
+
35
+ export interface CommandOptionsConfig {
36
+ /**
37
+ * The current working directory of the executed program or shell.
38
+ * If omitted VSCode's current workspace root is used.
39
+ */
40
+ cwd?: string;
41
+
42
+ /**
43
+ * The additional environment of the executed program or shell. If omitted
44
+ * the parent process' environment is used.
45
+ */
46
+ env?: IStringDictionary<string>;
47
+
48
+ /**
49
+ * The shell configuration;
50
+ */
51
+ shell?: TaskTypes.ShellConfiguration;
52
+ }
53
+
54
+ export interface PresentationOptionsConfig {
55
+ /**
56
+ * Controls whether the task output is reveal in the user interface.
57
+ * Defaults to `RevealKind.Always`.
58
+ */
59
+ reveal: TaskTypes.RevealKind;
60
+
61
+ /**
62
+ * Controls whether the problems pane is revealed when running this task or not.
63
+ * Defaults to `RevealProblemKind.Never`.
64
+ */
65
+ revealProblems: TaskTypes.RevealProblemKind;
66
+
67
+ /**
68
+ * Controls whether the command associated with the task is echoed
69
+ * in the user interface.
70
+ */
71
+ echo: boolean;
72
+
73
+ /**
74
+ * Controls whether the panel showing the task output is taking focus.
75
+ */
76
+ focus: boolean;
77
+
78
+ /**
79
+ * Controls if the task panel is used for this task only (dedicated),
80
+ * shared between tasks (shared) or if a new panel is created on
81
+ * every task execution (new). Defaults to `TaskInstanceKind.Shared`
82
+ */
83
+ panel: TaskTypes.PanelKind;
84
+
85
+ /**
86
+ * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message.
87
+ */
88
+ showReuseMessage: boolean;
89
+
90
+ /**
91
+ * Controls whether to clear the terminal before executing the task.
92
+ */
93
+ clear: boolean;
94
+
95
+ /**
96
+ * Controls whether the task is executed in a specific terminal group using split panes.
97
+ */
98
+ group?: string;
99
+ }
100
+
101
+ export interface RunOptionsConfig {
102
+ reevaluateOnRerun?: boolean;
103
+ runOn?: string;
104
+ }
105
+
106
+ export interface TaskIdentifier {
107
+ type?: string;
108
+ [name: string]: any;
109
+ }
110
+
111
+ export namespace TaskIdentifier {
112
+ export function is(value: any): value is TaskIdentifier {
113
+ const candidate: TaskIdentifier = value;
114
+ return candidate !== undefined && isString(value.type);
115
+ }
116
+ }
117
+
118
+ export interface LegacyTaskProperties {
119
+ /**
120
+ * @deprecated Use `isBackground` instead.
121
+ * Whether the executed command is kept alive and is watching the file system.
122
+ */
123
+ isWatching?: boolean;
124
+
125
+ /**
126
+ * @deprecated Use `group` instead.
127
+ * Whether this task maps to the default build command.
128
+ */
129
+ isBuildCommand?: boolean;
130
+
131
+ /**
132
+ * @deprecated Use `group` instead.
133
+ * Whether this task maps to the default test command.
134
+ */
135
+ isTestCommand?: boolean;
136
+ }
137
+
138
+ export interface LegacyCommandProperties {
139
+ /**
140
+ * Whether this is a shell or process
141
+ */
142
+ type?: string;
143
+
144
+ /**
145
+ * @deprecated Use presentation options
146
+ * Controls whether the output view of the running tasks is brought to front or not.
147
+ * See BaseTaskRunnerConfiguration#showOutput for details.
148
+ */
149
+ showOutput?: string;
150
+
151
+ /**
152
+ * @deprecated Use presentation options
153
+ * Controls whether the executed command is printed to the output windows as well.
154
+ */
155
+ echoCommand?: boolean;
156
+
157
+ /**
158
+ * @deprecated Use presentation instead
159
+ */
160
+ terminal?: PresentationOptionsConfig;
161
+
162
+ /**
163
+ * @deprecated Use inline commands.
164
+ * See BaseTaskRunnerConfiguration#suppressTaskName for details.
165
+ */
166
+ suppressTaskName?: boolean;
167
+
168
+ /**
169
+ * Some commands require that the task argument is highlighted with a special
170
+ * prefix (e.g. /t: for msbuild). This property can be used to control such
171
+ * a prefix.
172
+ */
173
+ taskSelector?: string;
174
+
175
+ /**
176
+ * @deprecated use the task type instead.
177
+ * Specifies whether the command is a shell command and therefore must
178
+ * be executed in a shell interpreter (e.g. cmd.exe, bash, ...).
179
+ *
180
+ * Defaults to false if omitted.
181
+ */
182
+ isShellCommand?: boolean | TaskTypes.ShellConfiguration;
183
+ }
184
+
185
+ export type CommandString = string | string[] | { value: string | string[]; quoting: 'escape' | 'strong' | 'weak' };
186
+
187
+ export namespace CommandString {
188
+ export function value(value: CommandString): string {
189
+ if (isString(value)) {
190
+ return value;
191
+ } else if (isStringArray(value)) {
192
+ return value.join(' ');
193
+ } else {
194
+ if (isString(value.value)) {
195
+ return value.value;
196
+ } else {
197
+ return value.value.join(' ');
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ export interface BaseCommandProperties {
204
+ /**
205
+ * The command to be executed. Can be an external program or a shell
206
+ * command.
207
+ */
208
+ command?: CommandString;
209
+
210
+ /**
211
+ * The command options used when the command is executed. Can be omitted.
212
+ */
213
+ options?: CommandOptionsConfig;
214
+
215
+ /**
216
+ * The arguments passed to the command or additional arguments passed to the
217
+ * command when using a global command.
218
+ */
219
+ args?: CommandString[];
220
+ }
221
+
222
+ export interface CommandProperties extends BaseCommandProperties {
223
+ /**
224
+ * Windows specific command properties
225
+ */
226
+ windows?: BaseCommandProperties;
227
+
228
+ /**
229
+ * OSX specific command properties
230
+ */
231
+ osx?: BaseCommandProperties;
232
+
233
+ /**
234
+ * linux specific command properties
235
+ */
236
+ linux?: BaseCommandProperties;
237
+ }
238
+
239
+ export interface GroupKind {
240
+ kind?: string;
241
+ isDefault?: boolean;
242
+ }
243
+
244
+ export interface ConfigurationProperties {
245
+ /**
246
+ * The task's name
247
+ */
248
+ taskName?: string;
249
+
250
+ /**
251
+ * The UI label used for the task.
252
+ */
253
+ label?: string;
254
+
255
+ /**
256
+ * An optional identifier which can be used to reference a task
257
+ * in a dependsOn or other attributes.
258
+ */
259
+ identifier?: string;
260
+
261
+ /**
262
+ * Whether the executed command is kept alive and runs in the background.
263
+ */
264
+ isBackground?: boolean;
265
+
266
+ /**
267
+ * Whether the task should prompt on close for confirmation if running.
268
+ */
269
+ promptOnClose?: boolean;
270
+
271
+ /**
272
+ * Defines the group the task belongs too.
273
+ */
274
+ group?: string | GroupKind;
275
+
276
+ /**
277
+ * The other tasks the task depend on
278
+ */
279
+ dependsOn?: string | TaskIdentifier | Array<string | TaskIdentifier>;
280
+
281
+ /**
282
+ * The order the dependsOn tasks should be executed in.
283
+ */
284
+ dependsOrder?: string;
285
+
286
+ /**
287
+ * Controls the behavior of the used terminal
288
+ */
289
+ presentation?: PresentationOptionsConfig;
290
+
291
+ /**
292
+ * Controls shell options.
293
+ */
294
+ options?: CommandOptionsConfig;
295
+
296
+ /**
297
+ * The problem matcher(s) to use to capture problems in the tasks
298
+ * output.
299
+ */
300
+ problemMatcher?: ProblemMatcherType;
301
+
302
+ /**
303
+ * Task run options. Control run related properties.
304
+ */
305
+ runOptions?: RunOptionsConfig;
306
+ }
307
+
308
+ export interface CustomTask extends CommandProperties, ConfigurationProperties {
309
+ /**
310
+ * Custom tasks have the type CUSTOMIZED_TASK_TYPE
311
+ */
312
+ type?: string;
313
+ }
314
+
315
+ export interface ConfiguringTask extends ConfigurationProperties {
316
+ /**
317
+ * The contributed type of the task
318
+ */
319
+ type?: string;
320
+ }
321
+
322
+ /**
323
+ * The base task runner configuration
324
+ */
325
+ export interface BaseTaskRunnerConfiguration {
326
+ /**
327
+ * The command to be executed. Can be an external program or a shell
328
+ * command.
329
+ */
330
+ command?: CommandString;
331
+
332
+ /**
333
+ * @deprecated Use type instead
334
+ *
335
+ * Specifies whether the command is a shell command and therefore must
336
+ * be executed in a shell interpreter (e.g. cmd.exe, bash, ...).
337
+ *
338
+ * Defaults to false if omitted.
339
+ */
340
+ isShellCommand?: boolean;
341
+
342
+ /**
343
+ * The task type
344
+ */
345
+ type?: string;
346
+
347
+ /**
348
+ * The command options used when the command is executed. Can be omitted.
349
+ */
350
+ options?: CommandOptionsConfig;
351
+
352
+ /**
353
+ * The arguments passed to the command. Can be omitted.
354
+ */
355
+ args?: CommandString[];
356
+
357
+ /**
358
+ * Controls whether the output view of the running tasks is brought to front or not.
359
+ * Valid values are:
360
+ * "always": bring the output window always to front when a task is executed.
361
+ * "silent": only bring it to front if no problem matcher is defined for the task executed.
362
+ * "never": never bring the output window to front.
363
+ *
364
+ * If omitted "always" is used.
365
+ */
366
+ showOutput?: string;
367
+
368
+ /**
369
+ * Controls whether the executed command is printed to the output windows as well.
370
+ */
371
+ echoCommand?: boolean;
372
+
373
+ /**
374
+ * The group
375
+ */
376
+ group?: string | GroupKind;
377
+ /**
378
+ * Controls the behavior of the used terminal
379
+ */
380
+ presentation?: PresentationOptionsConfig;
381
+
382
+ /**
383
+ * If set to false the task name is added as an additional argument to the
384
+ * command when executed. If set to true the task name is suppressed. If
385
+ * omitted false is used.
386
+ */
387
+ suppressTaskName?: boolean;
388
+
389
+ /**
390
+ * Some commands require that the task argument is highlighted with a special
391
+ * prefix (e.g. /t: for msbuild). This property can be used to control such
392
+ * a prefix.
393
+ */
394
+ taskSelector?: string;
395
+
396
+ /**
397
+ * The problem matcher(s) to used if a global command is executed (e.g. no tasks
398
+ * are defined). A json file can either contain a global problemMatcher
399
+ * property or a tasks property but not both.
400
+ */
401
+ problemMatcher?: ProblemMatcherType;
402
+
403
+ /**
404
+ * @deprecated Use `isBackground` instead.
405
+ *
406
+ * Specifies whether a global command is a watching the filesystem. A task.json
407
+ * file can either contain a global isWatching property or a tasks property
408
+ * but not both.
409
+ */
410
+ isWatching?: boolean;
411
+
412
+ /**
413
+ * Specifies whether a global command is a background task.
414
+ */
415
+ isBackground?: boolean;
416
+
417
+ /**
418
+ * Whether the task should prompt on close for confirmation if running.
419
+ */
420
+ promptOnClose?: boolean;
421
+
422
+ /**
423
+ * The configuration of the available A json file can either
424
+ * contain a global problemMatcher property or a tasks property but not both.
425
+ */
426
+ tasks?: Array<CustomTask | ConfiguringTask>;
427
+
428
+ /**
429
+ * Problem matcher declarations.
430
+ */
431
+ declares?: Config.NamedProblemMatcher[];
432
+
433
+ /**
434
+ * Optional user input variables.
435
+ */
436
+ inputs?: any[];
437
+ }
438
+
439
+ /**
440
+ * A configuration of an external build system. BuildConfiguration.buildSystem
441
+ * must be set to 'program'
442
+ */
443
+ export interface ExternalTaskRunnerConfiguration extends BaseTaskRunnerConfiguration {
444
+ _runner?: string;
445
+
446
+ /**
447
+ * Determines the runner to use
448
+ */
449
+ runner?: string;
450
+
451
+ /**
452
+ * The config's version number
453
+ */
454
+ version: string;
455
+
456
+ /**
457
+ * Windows specific task configuration
458
+ */
459
+ windows?: BaseTaskRunnerConfiguration;
460
+
461
+ /**
462
+ * Mac specific task configuration
463
+ */
464
+ osx?: BaseTaskRunnerConfiguration;
465
+
466
+ /**
467
+ * Linux specific task configuration
468
+ */
469
+ linux?: BaseTaskRunnerConfiguration;
470
+ }
471
+
472
+ enum ProblemMatcherKind {
473
+ Unknown,
474
+ String,
475
+ ProblemMatcher,
476
+ Array,
477
+ }
478
+
479
+ const EMPTY_ARRAY: any[] = [];
480
+ Object.freeze(EMPTY_ARRAY);
481
+
482
+ function assignProperty<T, K extends keyof T>(target: T, source: Partial<T>, key: K) {
483
+ const sourceAtKey = source[key];
484
+ if (sourceAtKey !== undefined) {
485
+ target[key] = sourceAtKey!;
486
+ }
487
+ }
488
+
489
+ function fillProperty<T, K extends keyof T>(target: T, source: Partial<T>, key: K) {
490
+ const sourceAtKey = source[key];
491
+ if (target[key] === undefined && sourceAtKey !== undefined) {
492
+ target[key] = sourceAtKey!;
493
+ }
494
+ }
495
+
496
+ interface ParserType<T> {
497
+ isEmpty(value: T | undefined): boolean;
498
+ assignProperties(target: T | undefined, source: T | undefined): T | undefined;
499
+ fillProperties(target: T | undefined, source: T | undefined): T | undefined;
500
+ fillDefaults(value: T | undefined, context: ParseContext): T | undefined;
501
+ freeze(value: T): Readonly<T> | undefined;
502
+ }
503
+
504
+ interface MetaData<T, U> {
505
+ property: keyof T;
506
+ type?: ParserType<U>;
507
+ }
508
+
509
+ function _isEmpty<T>(this: void, value: T | undefined, properties: MetaData<T, any>[] | undefined): boolean {
510
+ if (value === undefined || value === null || properties === undefined) {
511
+ return true;
512
+ }
513
+ for (const meta of properties) {
514
+ const property = value[meta.property];
515
+ if (property !== undefined && property !== null) {
516
+ if (meta.type !== undefined && !meta.type.isEmpty(property)) {
517
+ return false;
518
+ } else if (!Array.isArray(property) || property.length > 0) {
519
+ return false;
520
+ }
521
+ }
522
+ }
523
+ return true;
524
+ }
525
+
526
+ function _assignProperties<T>(
527
+ this: void,
528
+ target: T | undefined,
529
+ source: T | undefined,
530
+ properties: MetaData<T, any>[],
531
+ ): T | undefined {
532
+ if (!source || _isEmpty(source, properties)) {
533
+ return target;
534
+ }
535
+ if (!target || _isEmpty(target, properties)) {
536
+ return source;
537
+ }
538
+ for (const meta of properties) {
539
+ const property = meta.property;
540
+ let value: any;
541
+ if (meta.type !== undefined) {
542
+ value = meta.type.assignProperties(target[property], source[property]);
543
+ } else {
544
+ value = source[property];
545
+ }
546
+ if (value !== undefined && value !== null) {
547
+ target[property] = value;
548
+ }
549
+ }
550
+ return target;
551
+ }
552
+
553
+ function _fillProperties<T>(
554
+ this: void,
555
+ target: T | undefined,
556
+ source: T | undefined,
557
+ properties: MetaData<T, any>[] | undefined,
558
+ ): T | undefined {
559
+ if (!source || _isEmpty(source, properties)) {
560
+ return target;
561
+ }
562
+ if (!target || _isEmpty(target, properties)) {
563
+ return source;
564
+ }
565
+ for (const meta of properties!) {
566
+ const property = meta.property;
567
+ let value: any;
568
+ if (meta.type) {
569
+ value = meta.type.fillProperties(target[property], source[property]);
570
+ } else if (target[property] === undefined) {
571
+ value = source[property];
572
+ }
573
+ if (value !== undefined && value !== null) {
574
+ target[property] = value;
575
+ }
576
+ }
577
+ return target;
578
+ }
579
+
580
+ function _fillDefaults<T>(
581
+ this: void,
582
+ target: T | undefined,
583
+ defaults: T | undefined,
584
+ properties: MetaData<T, any>[],
585
+ context: ParseContext,
586
+ ): T | undefined {
587
+ if (target && Object.isFrozen(target)) {
588
+ return target;
589
+ }
590
+ if (target === undefined || target === null || defaults === undefined || defaults === null) {
591
+ if (defaults !== undefined && defaults !== null) {
592
+ return deepClone(defaults);
593
+ } else {
594
+ return undefined;
595
+ }
596
+ }
597
+ for (const meta of properties) {
598
+ const property = meta.property;
599
+ if (target[property] !== undefined) {
600
+ continue;
601
+ }
602
+ let value: any;
603
+ if (meta.type) {
604
+ value = meta.type.fillDefaults(target[property], context);
605
+ } else {
606
+ value = defaults[property];
607
+ }
608
+
609
+ if (value !== undefined && value !== null) {
610
+ target[property] = value;
611
+ }
612
+ }
613
+ return target;
614
+ }
615
+
616
+ function _freeze<T>(this: void, target: T, properties: MetaData<T, any>[]): Readonly<T> | undefined {
617
+ if (target === undefined || target === null) {
618
+ return undefined;
619
+ }
620
+ if (Object.isFrozen(target)) {
621
+ return target;
622
+ }
623
+ for (const meta of properties) {
624
+ if (meta.type) {
625
+ const value = target[meta.property];
626
+ if (value) {
627
+ meta.type.freeze(value);
628
+ }
629
+ }
630
+ }
631
+ Object.freeze(target);
632
+ return target;
633
+ }
634
+
635
+ export namespace RunOnOptions {
636
+ export function fromString(value: string | undefined): TaskTypes.RunOnOptions {
637
+ if (!value) {
638
+ return TaskTypes.RunOnOptions.default;
639
+ }
640
+ switch (value.toLowerCase()) {
641
+ case 'folderopen':
642
+ return TaskTypes.RunOnOptions.folderOpen;
643
+ case 'default':
644
+ default:
645
+ return TaskTypes.RunOnOptions.default;
646
+ }
647
+ }
648
+ }
649
+
650
+ export namespace RunOptions {
651
+ export function fromConfiguration(value: RunOptionsConfig | undefined): TaskTypes.RunOptions {
652
+ return {
653
+ reevaluateOnRerun: value ? value.reevaluateOnRerun : true,
654
+ runOn: value ? RunOnOptions.fromString(value.runOn) : TaskTypes.RunOnOptions.default,
655
+ };
656
+ }
657
+ }
658
+
659
+ type createTaskIdentifierFn = (
660
+ external: TaskIdentifier,
661
+ reporter: { error(message: string): void },
662
+ ) => KeyedTaskIdentifier | undefined;
663
+
664
+ export type getProblemMatcherFn = (name: string) => NamedProblemMatcher | undefined;
665
+
666
+ export type getProblemPatternFn = (name: string) => undefined | NamedProblemPattern | NamedProblemPattern[];
667
+
668
+ class ParseContext {
669
+ workspaceFolder: IWorkspaceFolder;
670
+ problemReporter: IProblemReporter;
671
+ namedProblemMatchers: { [name: string]: NamedProblemMatcher };
672
+ uuidMap: UUIDMap;
673
+ engine: TaskTypes.ExecutionEngine;
674
+ schemaVersion: TaskTypes.JsonSchemaVersion;
675
+ platform: Platform;
676
+ taskLoadIssues: string[];
677
+ createTaskIdentifier: createTaskIdentifierFn;
678
+ getProblemMatcher: getProblemMatcherFn;
679
+ getProblemPattern: getProblemPatternFn;
680
+ }
681
+
682
+ namespace ShellConfiguration {
683
+ const properties: MetaData<TaskTypes.ShellConfiguration, void>[] = [
684
+ { property: 'executable' },
685
+ { property: 'args' },
686
+ { property: 'quoting' },
687
+ ];
688
+
689
+ export function is(value: any): value is TaskTypes.ShellConfiguration {
690
+ const candidate: TaskTypes.ShellConfiguration = value;
691
+ return candidate && (isString(candidate.executable) || isStringArray(candidate.args));
692
+ }
693
+
694
+ export function from(
695
+ this: void,
696
+ config: TaskTypes.ShellConfiguration | undefined,
697
+ context: ParseContext,
698
+ ): TaskTypes.ShellConfiguration | undefined {
699
+ if (!is(config)) {
700
+ return undefined;
701
+ }
702
+ const result: TaskTypes.ShellConfiguration = {};
703
+ if (config.executable !== undefined) {
704
+ result.executable = config.executable;
705
+ }
706
+ if (config.args !== undefined) {
707
+ result.args = config.args.slice();
708
+ }
709
+ if (config.quoting !== undefined) {
710
+ result.quoting = deepClone(config.quoting);
711
+ }
712
+
713
+ return result;
714
+ }
715
+
716
+ export function isEmpty(this: void, value: TaskTypes.ShellConfiguration): boolean {
717
+ return _isEmpty(value, properties);
718
+ }
719
+
720
+ export function assignProperties(
721
+ this: void,
722
+ target: TaskTypes.ShellConfiguration | undefined,
723
+ source: TaskTypes.ShellConfiguration | undefined,
724
+ ): TaskTypes.ShellConfiguration | undefined {
725
+ return _assignProperties(target, source, properties);
726
+ }
727
+
728
+ export function fillProperties(
729
+ this: void,
730
+ target: TaskTypes.ShellConfiguration,
731
+ source: TaskTypes.ShellConfiguration,
732
+ ): TaskTypes.ShellConfiguration | undefined {
733
+ return _fillProperties(target, source, properties);
734
+ }
735
+
736
+ export function fillDefaults(
737
+ this: void,
738
+ value: TaskTypes.ShellConfiguration,
739
+ context: ParseContext,
740
+ ): TaskTypes.ShellConfiguration {
741
+ return value;
742
+ }
743
+
744
+ export function freeze(
745
+ this: void,
746
+ value: TaskTypes.ShellConfiguration,
747
+ ): Readonly<TaskTypes.ShellConfiguration> | undefined {
748
+ if (!value) {
749
+ return undefined;
750
+ }
751
+ return Object.freeze(value);
752
+ }
753
+ }
754
+
755
+ namespace CommandOptions {
756
+ const properties: MetaData<TaskTypes.CommandOptions, TaskTypes.ShellConfiguration>[] = [
757
+ { property: 'cwd' },
758
+ { property: 'env' },
759
+ { property: 'shell', type: ShellConfiguration },
760
+ ];
761
+ const defaults: CommandOptionsConfig = { cwd: '${workspaceFolder}' };
762
+
763
+ export function from(
764
+ this: void,
765
+ options: CommandOptionsConfig,
766
+ context: ParseContext,
767
+ ): TaskTypes.CommandOptions | undefined {
768
+ const result: TaskTypes.CommandOptions = {};
769
+ if (options.cwd !== undefined) {
770
+ if (isString(options.cwd)) {
771
+ result.cwd = options.cwd;
772
+ } else {
773
+ context.taskLoadIssues.push(
774
+ formatLocalize(
775
+ 'ConfigurationParser.invalidCWD',
776
+ 'Warning: options.cwd must be of type string. Ignoring value {0}\n',
777
+ options.cwd,
778
+ ),
779
+ );
780
+ }
781
+ }
782
+ if (options.env !== undefined) {
783
+ result.env = deepClone(options.env);
784
+ }
785
+ result.shell = ShellConfiguration.from(options.shell, context);
786
+ return isEmpty(result) ? undefined : result;
787
+ }
788
+
789
+ export function isEmpty(value: TaskTypes.CommandOptions | undefined): boolean {
790
+ return _isEmpty(value, properties);
791
+ }
792
+
793
+ export function assignProperties(
794
+ target: TaskTypes.CommandOptions | undefined,
795
+ source: TaskTypes.CommandOptions | undefined,
796
+ ): TaskTypes.CommandOptions | undefined {
797
+ if (source === undefined || isEmpty(source)) {
798
+ return target;
799
+ }
800
+ if (target === undefined || isEmpty(target)) {
801
+ return source;
802
+ }
803
+ assignProperty(target, source, 'cwd');
804
+ if (target.env === undefined) {
805
+ target.env = source.env;
806
+ } else if (source.env !== undefined) {
807
+ const env: { [key: string]: string } = Object.create(null);
808
+ if (target.env !== undefined) {
809
+ Object.keys(target.env).forEach((key) => (env[key] = target.env![key]));
810
+ }
811
+ if (source.env !== undefined) {
812
+ Object.keys(source.env).forEach((key) => (env[key] = source.env![key]));
813
+ }
814
+ target.env = env;
815
+ }
816
+ target.shell = ShellConfiguration.assignProperties(target.shell, source.shell);
817
+ return target;
818
+ }
819
+
820
+ export function fillProperties(
821
+ target: TaskTypes.CommandOptions | undefined,
822
+ source: TaskTypes.CommandOptions | undefined,
823
+ ): TaskTypes.CommandOptions | undefined {
824
+ return _fillProperties(target, source, properties);
825
+ }
826
+
827
+ export function fillDefaults(
828
+ value: TaskTypes.CommandOptions | undefined,
829
+ context: ParseContext,
830
+ ): TaskTypes.CommandOptions | undefined {
831
+ return _fillDefaults(value, defaults, properties, context);
832
+ }
833
+
834
+ export function freeze(value: TaskTypes.CommandOptions): Readonly<TaskTypes.CommandOptions> | undefined {
835
+ return _freeze(value, properties);
836
+ }
837
+ }
838
+
839
+ namespace CommandConfiguration {
840
+ export namespace PresentationOptions {
841
+ const properties: MetaData<TaskTypes.PresentationOptions, void>[] = [
842
+ { property: 'echo' },
843
+ { property: 'reveal' },
844
+ { property: 'revealProblems' },
845
+ { property: 'focus' },
846
+ { property: 'panel' },
847
+ { property: 'showReuseMessage' },
848
+ { property: 'clear' },
849
+ { property: 'group' },
850
+ ];
851
+
852
+ interface PresentationOptionsShape extends LegacyCommandProperties {
853
+ presentation?: PresentationOptionsConfig;
854
+ }
855
+
856
+ export function from(
857
+ this: void,
858
+ config: PresentationOptionsShape,
859
+ context: ParseContext,
860
+ ): TaskTypes.PresentationOptions | undefined {
861
+ let echo: boolean;
862
+ let reveal: TaskTypes.RevealKind;
863
+ let revealProblems: TaskTypes.RevealProblemKind;
864
+ let focus: boolean;
865
+ let panel: TaskTypes.PanelKind;
866
+ let showReuseMessage: boolean;
867
+ let clear: boolean;
868
+ let group: string | undefined;
869
+ let hasProps = false;
870
+ if (isBoolean(config.echoCommand)) {
871
+ echo = config.echoCommand;
872
+ hasProps = true;
873
+ }
874
+ if (isString(config.showOutput)) {
875
+ reveal = TaskTypes.RevealKind.fromString(config.showOutput);
876
+ hasProps = true;
877
+ }
878
+ const presentation = config.presentation || config.terminal;
879
+ if (presentation) {
880
+ if (isBoolean(presentation.echo)) {
881
+ echo = presentation.echo;
882
+ }
883
+ if (isString(presentation.reveal)) {
884
+ reveal = TaskTypes.RevealKind.fromString(presentation.reveal);
885
+ }
886
+ if (isString(presentation.revealProblems)) {
887
+ revealProblems = TaskTypes.RevealProblemKind.fromString(presentation.revealProblems);
888
+ }
889
+ if (isBoolean(presentation.focus)) {
890
+ focus = presentation.focus;
891
+ }
892
+ if (isString(presentation.panel)) {
893
+ panel = TaskTypes.PanelKind.fromString(presentation.panel);
894
+ }
895
+ if (isBoolean(presentation.showReuseMessage)) {
896
+ showReuseMessage = presentation.showReuseMessage;
897
+ }
898
+ if (isBoolean(presentation.clear)) {
899
+ clear = presentation.clear;
900
+ }
901
+ if (isString(presentation.group)) {
902
+ group = presentation.group;
903
+ }
904
+ hasProps = true;
905
+ }
906
+ if (!hasProps) {
907
+ return undefined;
908
+ }
909
+ return {
910
+ echo: echo!,
911
+ reveal: reveal!,
912
+ revealProblems: revealProblems!,
913
+ focus: focus!,
914
+ panel: panel!,
915
+ showReuseMessage: showReuseMessage!,
916
+ clear: clear!,
917
+ group,
918
+ };
919
+ }
920
+
921
+ export function assignProperties(
922
+ target: TaskTypes.PresentationOptions,
923
+ source: TaskTypes.PresentationOptions | undefined,
924
+ ): TaskTypes.PresentationOptions | undefined {
925
+ return _assignProperties(target, source, properties);
926
+ }
927
+
928
+ export function fillProperties(
929
+ target: TaskTypes.PresentationOptions,
930
+ source: TaskTypes.PresentationOptions | undefined,
931
+ ): TaskTypes.PresentationOptions | undefined {
932
+ return _fillProperties(target, source, properties);
933
+ }
934
+
935
+ export function fillDefaults(
936
+ value: TaskTypes.PresentationOptions,
937
+ context: ParseContext,
938
+ ): TaskTypes.PresentationOptions | undefined {
939
+ const defaultEcho = context.engine === TaskTypes.ExecutionEngine.Terminal ? true : false;
940
+ return _fillDefaults(
941
+ value,
942
+ {
943
+ echo: defaultEcho,
944
+ reveal: TaskTypes.RevealKind.Always,
945
+ revealProblems: TaskTypes.RevealProblemKind.Never,
946
+ focus: false,
947
+ panel: TaskTypes.PanelKind.Shared,
948
+ showReuseMessage: true,
949
+ clear: false,
950
+ },
951
+ properties,
952
+ context,
953
+ );
954
+ }
955
+
956
+ export function freeze(value: TaskTypes.PresentationOptions): Readonly<TaskTypes.PresentationOptions> | undefined {
957
+ return _freeze(value, properties);
958
+ }
959
+
960
+ export function isEmpty(this: void, value: TaskTypes.PresentationOptions): boolean {
961
+ return _isEmpty(value, properties);
962
+ }
963
+ }
964
+
965
+ namespace ShellString {
966
+ export function from(this: void, value: CommandString | undefined): TaskTypes.CommandString | undefined {
967
+ if (value === undefined || value === null) {
968
+ return undefined;
969
+ }
970
+ if (isString(value)) {
971
+ return value;
972
+ } else if (isStringArray(value)) {
973
+ return value.join(' ');
974
+ } else {
975
+ const quoting = TaskTypes.ShellQuoting.from(value.quoting);
976
+ const result = isString(value.value)
977
+ ? value.value
978
+ : isStringArray(value.value)
979
+ ? value.value.join(' ')
980
+ : undefined;
981
+ if (result) {
982
+ return {
983
+ value: result,
984
+ quoting,
985
+ };
986
+ } else {
987
+ return undefined;
988
+ }
989
+ }
990
+ }
991
+ }
992
+
993
+ interface BaseCommandConfigurationShape extends BaseCommandProperties, LegacyCommandProperties {}
994
+
995
+ interface CommandConfigurationShape extends BaseCommandConfigurationShape {
996
+ windows?: BaseCommandConfigurationShape;
997
+ osx?: BaseCommandConfigurationShape;
998
+ linux?: BaseCommandConfigurationShape;
999
+ }
1000
+
1001
+ const properties: MetaData<TaskTypes.CommandConfiguration, any>[] = [
1002
+ { property: 'runtime' },
1003
+ { property: 'name' },
1004
+ { property: 'options', type: CommandOptions },
1005
+ { property: 'args' },
1006
+ { property: 'taskSelector' },
1007
+ { property: 'suppressTaskName' },
1008
+ { property: 'presentation', type: PresentationOptions },
1009
+ ];
1010
+
1011
+ export function from(
1012
+ this: void,
1013
+ config: CommandConfigurationShape,
1014
+ context: ParseContext,
1015
+ ): TaskTypes.CommandConfiguration | undefined {
1016
+ let result: TaskTypes.CommandConfiguration = fromBase(config, context)!;
1017
+
1018
+ let osConfig: TaskTypes.CommandConfiguration | undefined;
1019
+ if (config.windows && context.platform === Platform.Windows) {
1020
+ osConfig = fromBase(config.windows, context);
1021
+ } else if (config.osx && context.platform === Platform.Mac) {
1022
+ osConfig = fromBase(config.osx, context);
1023
+ } else if (config.linux && context.platform === Platform.Linux) {
1024
+ osConfig = fromBase(config.linux, context);
1025
+ }
1026
+ if (osConfig) {
1027
+ result = assignProperties(result, osConfig, context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0);
1028
+ }
1029
+ return isEmpty(result) ? undefined : result;
1030
+ }
1031
+
1032
+ function fromBase(
1033
+ this: void,
1034
+ config: BaseCommandConfigurationShape,
1035
+ context: ParseContext,
1036
+ ): TaskTypes.CommandConfiguration | undefined {
1037
+ const name: TaskTypes.CommandString | undefined = ShellString.from(config.command);
1038
+ let runtime: TaskTypes.RuntimeType;
1039
+ if (isString(config.type)) {
1040
+ if (config.type === 'shell' || config.type === 'process') {
1041
+ runtime = TaskTypes.RuntimeType.fromString(config.type);
1042
+ }
1043
+ }
1044
+ const isShellConfiguration = ShellConfiguration.is(config.isShellCommand);
1045
+ if (isBoolean(config.isShellCommand) || isShellConfiguration) {
1046
+ runtime = TaskTypes.RuntimeType.Shell;
1047
+ } else if (config.isShellCommand !== undefined) {
1048
+ runtime = config.isShellCommand ? TaskTypes.RuntimeType.Shell : TaskTypes.RuntimeType.Process;
1049
+ }
1050
+
1051
+ const result: TaskTypes.CommandConfiguration = {
1052
+ name,
1053
+ runtime: runtime!,
1054
+ presentation: PresentationOptions.from(config, context)!,
1055
+ };
1056
+
1057
+ if (config.args !== undefined) {
1058
+ result.args = [];
1059
+ for (const arg of config.args) {
1060
+ const converted = ShellString.from(arg);
1061
+ if (converted !== undefined) {
1062
+ result.args.push(converted);
1063
+ } else {
1064
+ context.taskLoadIssues.push(
1065
+ formatLocalize(
1066
+ 'ConfigurationParser.inValidArg',
1067
+ 'Error: command argument must either be a string or a quoted string. Provided value is:\n{0}',
1068
+ arg ? JSON.stringify(arg, undefined, 4) : 'undefined',
1069
+ ),
1070
+ );
1071
+ }
1072
+ }
1073
+ }
1074
+ if (config.options !== undefined) {
1075
+ result.options = CommandOptions.from(config.options, context);
1076
+ if (result.options && result.options.shell === undefined && isShellConfiguration) {
1077
+ result.options.shell = ShellConfiguration.from(config.isShellCommand as TaskTypes.ShellConfiguration, context);
1078
+ if (context.engine !== TaskTypes.ExecutionEngine.Terminal) {
1079
+ context.taskLoadIssues.push(
1080
+ formatLocalize(
1081
+ 'ConfigurationParser.noShell',
1082
+ 'Warning: shell configuration is only supported when executing tasks in the terminal.',
1083
+ ),
1084
+ );
1085
+ }
1086
+ }
1087
+ }
1088
+
1089
+ if (isString(config.taskSelector)) {
1090
+ result.taskSelector = config.taskSelector;
1091
+ }
1092
+ if (isBoolean(config.suppressTaskName)) {
1093
+ result.suppressTaskName = config.suppressTaskName;
1094
+ }
1095
+
1096
+ return isEmpty(result) ? undefined : result;
1097
+ }
1098
+
1099
+ export function hasCommand(value: TaskTypes.CommandConfiguration): boolean {
1100
+ return value && !!value.name;
1101
+ }
1102
+
1103
+ export function isEmpty(value: TaskTypes.CommandConfiguration | undefined): boolean {
1104
+ return _isEmpty(value, properties);
1105
+ }
1106
+
1107
+ export function assignProperties(
1108
+ target: TaskTypes.CommandConfiguration,
1109
+ source: TaskTypes.CommandConfiguration,
1110
+ overwriteArgs: boolean,
1111
+ ): TaskTypes.CommandConfiguration {
1112
+ if (isEmpty(source)) {
1113
+ return target;
1114
+ }
1115
+ if (isEmpty(target)) {
1116
+ return source;
1117
+ }
1118
+ assignProperty(target, source, 'name');
1119
+ assignProperty(target, source, 'runtime');
1120
+ assignProperty(target, source, 'taskSelector');
1121
+ assignProperty(target, source, 'suppressTaskName');
1122
+ if (source.args !== undefined) {
1123
+ if (target.args === undefined || overwriteArgs) {
1124
+ target.args = source.args;
1125
+ } else {
1126
+ target.args = target.args.concat(source.args);
1127
+ }
1128
+ }
1129
+ target.presentation = PresentationOptions.assignProperties(target.presentation!, source.presentation)!;
1130
+ target.options = CommandOptions.assignProperties(target.options, source.options);
1131
+ return target;
1132
+ }
1133
+
1134
+ export function fillProperties(
1135
+ target: TaskTypes.CommandConfiguration,
1136
+ source: TaskTypes.CommandConfiguration,
1137
+ ): TaskTypes.CommandConfiguration | undefined {
1138
+ return _fillProperties(target, source, properties);
1139
+ }
1140
+
1141
+ export function fillGlobals(
1142
+ target: TaskTypes.CommandConfiguration,
1143
+ source: TaskTypes.CommandConfiguration | undefined,
1144
+ taskName: string | undefined,
1145
+ ): TaskTypes.CommandConfiguration {
1146
+ if (source === undefined || isEmpty(source)) {
1147
+ return target;
1148
+ }
1149
+ target = target || {
1150
+ name: undefined,
1151
+ runtime: undefined,
1152
+ presentation: undefined,
1153
+ };
1154
+ if (target.name === undefined) {
1155
+ fillProperty(target, source, 'name');
1156
+ fillProperty(target, source, 'taskSelector');
1157
+ fillProperty(target, source, 'suppressTaskName');
1158
+ let args: TaskTypes.CommandString[] = source.args ? source.args.slice() : [];
1159
+ if (!target.suppressTaskName && taskName) {
1160
+ if (target.taskSelector !== undefined) {
1161
+ args.push(target.taskSelector + taskName);
1162
+ } else {
1163
+ args.push(taskName);
1164
+ }
1165
+ }
1166
+ if (target.args) {
1167
+ args = args.concat(target.args);
1168
+ }
1169
+ target.args = args;
1170
+ }
1171
+ fillProperty(target, source, 'runtime');
1172
+
1173
+ target.presentation = PresentationOptions.fillProperties(target.presentation!, source.presentation)!;
1174
+ target.options = CommandOptions.fillProperties(target.options, source.options);
1175
+
1176
+ return target;
1177
+ }
1178
+
1179
+ export function fillDefaults(value: TaskTypes.CommandConfiguration | undefined, context: ParseContext): void {
1180
+ if (!value || Object.isFrozen(value)) {
1181
+ return;
1182
+ }
1183
+ if (value.name !== undefined && value.runtime === undefined) {
1184
+ value.runtime = TaskTypes.RuntimeType.Process;
1185
+ }
1186
+ value.presentation = PresentationOptions.fillDefaults(value.presentation!, context)!;
1187
+ if (!isEmpty(value)) {
1188
+ value.options = CommandOptions.fillDefaults(value.options, context);
1189
+ }
1190
+ if (value.args === undefined) {
1191
+ value.args = EMPTY_ARRAY;
1192
+ }
1193
+ if (value.suppressTaskName === undefined) {
1194
+ value.suppressTaskName = context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0;
1195
+ }
1196
+ }
1197
+
1198
+ export function freeze(value: TaskTypes.CommandConfiguration): Readonly<TaskTypes.CommandConfiguration> | undefined {
1199
+ return _freeze(value, properties);
1200
+ }
1201
+ }
1202
+
1203
+ namespace ProblemMatcherConverter {
1204
+ export function namedFrom(
1205
+ this: void,
1206
+ declares: Config.NamedProblemMatcher[] | undefined,
1207
+ context: ParseContext,
1208
+ ): IStringDictionary<NamedProblemMatcher> {
1209
+ const result: IStringDictionary<NamedProblemMatcher> = Object.create(null);
1210
+
1211
+ if (!isArray(declares)) {
1212
+ return result;
1213
+ }
1214
+ (declares as Config.NamedProblemMatcher[]).forEach((value) => {
1215
+ const namedProblemMatcher = new ProblemMatcherParser(context.problemReporter).parse(
1216
+ value,
1217
+ context.getProblemPattern,
1218
+ context.getProblemMatcher,
1219
+ );
1220
+ // @ts-ignore
1221
+ if (Config.isNamedProblemMatcher(namedProblemMatcher)) {
1222
+ result[namedProblemMatcher!.name] = namedProblemMatcher;
1223
+ } else {
1224
+ context.problemReporter.error(
1225
+ formatLocalize(
1226
+ 'ConfigurationParser.noName',
1227
+ 'Error: Problem Matcher in declare scope must have a name:\n{0}\n',
1228
+ JSON.stringify(value, undefined, 4),
1229
+ ),
1230
+ );
1231
+ }
1232
+ });
1233
+ return result;
1234
+ }
1235
+
1236
+ export function from(this: void, config: ProblemMatcherType | undefined, context: ParseContext): ProblemMatcher[] {
1237
+ const result: ProblemMatcher[] = [];
1238
+ if (config === undefined) {
1239
+ return result;
1240
+ }
1241
+ const kind = getProblemMatcherKind(config);
1242
+ if (kind === ProblemMatcherKind.Unknown) {
1243
+ context.problemReporter.warn(
1244
+ formatLocalize(
1245
+ 'ConfigurationParser.unknownMatcherKind',
1246
+ 'Warning: the defined problem matcher is unknown. Supported types are string | ProblemMatcher | Array<string | ProblemMatcher>.\n{0}\n',
1247
+ JSON.stringify(config, null, 4),
1248
+ ),
1249
+ );
1250
+ return result;
1251
+ } else if (kind === ProblemMatcherKind.String || kind === ProblemMatcherKind.ProblemMatcher) {
1252
+ const matcher = resolveProblemMatcher(config as ProblemMatcher, context);
1253
+ if (matcher) {
1254
+ result.push(matcher);
1255
+ }
1256
+ } else if (kind === ProblemMatcherKind.Array) {
1257
+ const problemMatchers = config as (string | ProblemMatcher)[];
1258
+ problemMatchers.forEach((problemMatcher) => {
1259
+ const matcher = resolveProblemMatcher(problemMatcher, context);
1260
+ if (matcher) {
1261
+ result.push(matcher);
1262
+ }
1263
+ });
1264
+ }
1265
+ return result;
1266
+ }
1267
+
1268
+ function getProblemMatcherKind(this: void, value: ProblemMatcherType): ProblemMatcherKind {
1269
+ if (isString(value)) {
1270
+ return ProblemMatcherKind.String;
1271
+ } else if (isArray(value)) {
1272
+ return ProblemMatcherKind.Array;
1273
+ } else if (!isUndefined(value)) {
1274
+ return ProblemMatcherKind.ProblemMatcher;
1275
+ } else {
1276
+ return ProblemMatcherKind.Unknown;
1277
+ }
1278
+ }
1279
+
1280
+ function resolveProblemMatcher(
1281
+ this: void,
1282
+ value: string | ProblemMatcher,
1283
+ context: ParseContext,
1284
+ ): ProblemMatcher | undefined {
1285
+ if (isString(value)) {
1286
+ let variableName = value as string;
1287
+ if (variableName.length > 1 && variableName[0] === '$') {
1288
+ variableName = variableName.substring(1);
1289
+ const global = context.getProblemMatcher(variableName);
1290
+ if (global) {
1291
+ return deepClone(global);
1292
+ }
1293
+ let localProblemMatcher = context.namedProblemMatchers[variableName];
1294
+ if (localProblemMatcher) {
1295
+ localProblemMatcher = deepClone(localProblemMatcher);
1296
+ // remove the name attr
1297
+ // 让他从一个 NamedProblemMatcher 到 ProblemMatcher
1298
+ delete (localProblemMatcher as Partial<NamedProblemMatcher>).name;
1299
+ return localProblemMatcher;
1300
+ }
1301
+ }
1302
+ context.taskLoadIssues.push(
1303
+ formatLocalize(
1304
+ 'ConfigurationParser.invalidVariableReference',
1305
+ 'Error: Invalid problemMatcher reference: {0}\n',
1306
+ value,
1307
+ ),
1308
+ );
1309
+ return undefined;
1310
+ } else {
1311
+ const json = value as Config.ProblemMatcher;
1312
+ return new ProblemMatcherParser(context.problemReporter).parse(
1313
+ json,
1314
+ context.getProblemPattern,
1315
+ context.getProblemMatcher,
1316
+ );
1317
+ }
1318
+ }
1319
+ }
1320
+
1321
+ const source: Partial<TaskTypes.TaskSource> = {
1322
+ kind: TaskTypes.TaskSourceKind.Workspace,
1323
+ label: 'Workspace',
1324
+ config: undefined,
1325
+ };
1326
+
1327
+ namespace GroupKind {
1328
+ export function from(
1329
+ this: void,
1330
+ external: string | GroupKind | undefined,
1331
+ ): [string, TaskTypes.GroupType] | undefined {
1332
+ if (external === undefined) {
1333
+ return undefined;
1334
+ }
1335
+ if (isString(external)) {
1336
+ if (TaskTypes.TaskGroup.is(external)) {
1337
+ return [external, TaskTypes.GroupType.user];
1338
+ } else {
1339
+ return undefined;
1340
+ }
1341
+ }
1342
+ if (!isString(external.kind) || !TaskTypes.TaskGroup.is(external.kind)) {
1343
+ return undefined;
1344
+ }
1345
+ const group: string = external.kind;
1346
+ const isDefault = !!external.isDefault;
1347
+
1348
+ return [group, isDefault ? TaskTypes.GroupType.default : TaskTypes.GroupType.user];
1349
+ }
1350
+ }
1351
+
1352
+ namespace TaskDependency {
1353
+ export function from(
1354
+ this: void,
1355
+ external: string | TaskIdentifier,
1356
+ context: ParseContext,
1357
+ ): TaskTypes.TaskDependency | undefined {
1358
+ if (isString(external)) {
1359
+ return { workspaceFolder: context.workspaceFolder, task: external };
1360
+ } else if (TaskIdentifier.is(external)) {
1361
+ return {
1362
+ workspaceFolder: context.workspaceFolder,
1363
+ task: context.createTaskIdentifier(external as TaskTypes.TaskIdentifier, context.problemReporter),
1364
+ };
1365
+ } else {
1366
+ return undefined;
1367
+ }
1368
+ }
1369
+ }
1370
+
1371
+ namespace DependsOrder {
1372
+ export function from(order: string | undefined): TaskTypes.DependsOrder {
1373
+ switch (order) {
1374
+ case TaskTypes.DependsOrder.sequence:
1375
+ return TaskTypes.DependsOrder.sequence;
1376
+ case TaskTypes.DependsOrder.parallel:
1377
+ default:
1378
+ return TaskTypes.DependsOrder.parallel;
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ namespace ConfigurationProperties {
1384
+ const properties: MetaData<TaskTypes.ConfigurationProperties, any>[] = [
1385
+ { property: 'name' },
1386
+ { property: 'identifier' },
1387
+ { property: 'group' },
1388
+ { property: 'isBackground' },
1389
+ { property: 'promptOnClose' },
1390
+ { property: 'dependsOn' },
1391
+ { property: 'presentation', type: CommandConfiguration.PresentationOptions },
1392
+ { property: 'problemMatchers' },
1393
+ ];
1394
+
1395
+ export function from(
1396
+ this: void,
1397
+ external: ConfigurationProperties & { [key: string]: any },
1398
+ context: ParseContext,
1399
+ includeCommandOptions: boolean,
1400
+ properties?: IJSONSchemaMap,
1401
+ ): TaskTypes.ConfigurationProperties | undefined {
1402
+ if (!external) {
1403
+ return undefined;
1404
+ }
1405
+ const result: TaskTypes.ConfigurationProperties = {};
1406
+
1407
+ if (properties) {
1408
+ for (const propertyName of Object.keys(properties)) {
1409
+ if (external[propertyName] !== undefined) {
1410
+ result[propertyName] = deepClone(external[propertyName]);
1411
+ }
1412
+ }
1413
+ }
1414
+
1415
+ if (isString(external.taskName)) {
1416
+ result.name = external.taskName;
1417
+ }
1418
+ if (isString(external.label) && context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0) {
1419
+ result.name = external.label;
1420
+ }
1421
+ if (isString(external.identifier)) {
1422
+ result.identifier = external.identifier;
1423
+ }
1424
+ if (external.isBackground !== undefined) {
1425
+ result.isBackground = !!external.isBackground;
1426
+ }
1427
+ if (external.promptOnClose !== undefined) {
1428
+ result.promptOnClose = !!external.promptOnClose;
1429
+ }
1430
+ if (external.group !== undefined) {
1431
+ if (isString(external.group) && TaskTypes.TaskGroup.is(external.group)) {
1432
+ result.group = external.group;
1433
+ result.groupType = TaskTypes.GroupType.user;
1434
+ } else {
1435
+ const values = GroupKind.from(external.group);
1436
+ if (values) {
1437
+ result.group = values[0];
1438
+ result.groupType = values[1];
1439
+ }
1440
+ }
1441
+ }
1442
+ if (external.dependsOn !== undefined) {
1443
+ if (isArray(external.dependsOn)) {
1444
+ result.dependsOn = external.dependsOn.reduce(
1445
+ (dependencies: TaskTypes.TaskDependency[], item): TaskTypes.TaskDependency[] => {
1446
+ const dependency = TaskDependency.from(item, context);
1447
+ if (dependency) {
1448
+ dependencies.push(dependency);
1449
+ }
1450
+ return dependencies;
1451
+ },
1452
+ [],
1453
+ );
1454
+ } else {
1455
+ const dependsOnValue = TaskDependency.from(external.dependsOn, context);
1456
+ result.dependsOn = dependsOnValue ? [dependsOnValue] : undefined;
1457
+ }
1458
+ }
1459
+ result.dependsOrder = DependsOrder.from(external.dependsOrder);
1460
+ if (
1461
+ includeCommandOptions &&
1462
+ (external.presentation !== undefined || (external as LegacyCommandProperties).terminal !== undefined)
1463
+ ) {
1464
+ result.presentation = CommandConfiguration.PresentationOptions.from(external, context);
1465
+ }
1466
+ if (includeCommandOptions && external.options !== undefined) {
1467
+ result.options = CommandOptions.from(external.options, context);
1468
+ }
1469
+ if (external.problemMatcher) {
1470
+ result.problemMatchers = ProblemMatcherConverter.from(external.problemMatcher, context);
1471
+ }
1472
+ return isEmpty(result) ? undefined : result;
1473
+ }
1474
+
1475
+ export function isEmpty(this: void, value: TaskTypes.ConfigurationProperties): boolean {
1476
+ return _isEmpty(value, properties);
1477
+ }
1478
+ }
1479
+
1480
+ namespace ConfiguringTask {
1481
+ const grunt = 'grunt.';
1482
+ const jake = 'jake.';
1483
+ const gulp = 'gulp.';
1484
+ const npm = 'vscode.npm.';
1485
+ const typescript = 'vscode.typescript.';
1486
+
1487
+ interface CustomizeShape {
1488
+ customize: string;
1489
+ }
1490
+
1491
+ export function from(
1492
+ this: void,
1493
+ external: ConfiguringTask,
1494
+ context: ParseContext,
1495
+ index: number,
1496
+ taskDefinitionRegister,
1497
+ ): TaskTypes.ConfiguringTask | undefined {
1498
+ if (!external) {
1499
+ return undefined;
1500
+ }
1501
+ const type = external.type;
1502
+ const customize = (external as CustomizeShape).customize;
1503
+ if (!type && !customize) {
1504
+ context.problemReporter.error(
1505
+ formatLocalize(
1506
+ 'ConfigurationParser.noTaskType',
1507
+ 'Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n',
1508
+ JSON.stringify(external, null, 4),
1509
+ ),
1510
+ );
1511
+ return undefined;
1512
+ }
1513
+ const typeDeclaration = type ? taskDefinitionRegister.get(type) : undefined;
1514
+ if (!typeDeclaration) {
1515
+ const message = formatLocalize(
1516
+ 'ConfigurationParser.noTypeDefinition',
1517
+ "Error: there is no registered task type '{0}'. Did you miss to install an extension that provides a corresponding task provider?",
1518
+ type,
1519
+ );
1520
+ context.problemReporter.error(message);
1521
+ return undefined;
1522
+ }
1523
+ let identifier: TaskIdentifier | undefined;
1524
+ if (isString(customize)) {
1525
+ if (customize.indexOf(grunt) === 0) {
1526
+ identifier = { type: 'grunt', task: customize.substring(grunt.length) };
1527
+ } else if (customize.indexOf(jake) === 0) {
1528
+ identifier = { type: 'jake', task: customize.substring(jake.length) };
1529
+ } else if (customize.indexOf(gulp) === 0) {
1530
+ identifier = { type: 'gulp', task: customize.substring(gulp.length) };
1531
+ } else if (customize.indexOf(npm) === 0) {
1532
+ identifier = { type: 'npm', script: customize.substring(npm.length + 4) };
1533
+ } else if (customize.indexOf(typescript) === 0) {
1534
+ identifier = { type: 'typescript', tsconfig: customize.substring(typescript.length + 6) };
1535
+ }
1536
+ } else {
1537
+ if (isString(external.type)) {
1538
+ identifier = external as TaskIdentifier;
1539
+ }
1540
+ }
1541
+ if (identifier === undefined) {
1542
+ context.problemReporter.error(
1543
+ formatLocalize(
1544
+ 'ConfigurationParsTaskTypes.er.missingType',
1545
+ "Error: the task configuration '{0}' is missing the required property 'type'. The task configuration will be ignored.",
1546
+ JSON.stringify(external, undefined, 0),
1547
+ ),
1548
+ );
1549
+ return undefined;
1550
+ }
1551
+ const taskIdentifier: KeyedTaskIdentifier | undefined = context.createTaskIdentifier(
1552
+ identifier,
1553
+ context.problemReporter,
1554
+ );
1555
+ if (taskIdentifier === undefined) {
1556
+ context.problemReporter.error(
1557
+ formatLocalize(
1558
+ 'ConfigurationParTaskTypes.ser.incorrectType',
1559
+ "Error: the task configuration '{0}' is using an unknown type. The task configuration will be ignored.",
1560
+ JSON.stringify(external, undefined, 0),
1561
+ ),
1562
+ );
1563
+ return undefined;
1564
+ }
1565
+ const configElement: TaskTypes.TaskSourceConfigElement = {
1566
+ workspaceFolder: context.workspaceFolder,
1567
+ file: '.vscode/json',
1568
+ index,
1569
+ element: external,
1570
+ };
1571
+ const result: TaskTypes.ConfiguringTask = new TaskTypes.ConfiguringTask(
1572
+ `${typeDeclaration.extensionId}.${taskIdentifier._key}`,
1573
+ Object.assign({} as TaskTypes.WorkspaceTaskSource, source, { config: configElement }),
1574
+ undefined,
1575
+ type,
1576
+ taskIdentifier,
1577
+ RunOptions.fromConfiguration(external.runOptions),
1578
+ {},
1579
+ );
1580
+ const configuration = ConfigurationProperties.from(external, context, true, typeDeclaration.properties);
1581
+ if (configuration) {
1582
+ result.configurationProperties = Object.assign(result.configurationProperties, configuration);
1583
+ if (result.configurationProperties.name) {
1584
+ result._label = result.configurationProperties.name;
1585
+ } else {
1586
+ let label = result.configures.type;
1587
+ if (typeDeclaration.required && typeDeclaration.required.length > 0) {
1588
+ for (const required of typeDeclaration.required) {
1589
+ const value = result.configures[required];
1590
+ if (value) {
1591
+ label = label + ' ' + value;
1592
+ break;
1593
+ }
1594
+ }
1595
+ }
1596
+ result._label = label;
1597
+ }
1598
+ if (!result.configurationProperties.identifier) {
1599
+ result.configurationProperties.identifier = taskIdentifier._key;
1600
+ }
1601
+ }
1602
+ return result;
1603
+ }
1604
+ }
1605
+
1606
+ namespace CustomTask {
1607
+ export function from(
1608
+ this: void,
1609
+ external: CustomTask,
1610
+ context: ParseContext,
1611
+ index: number,
1612
+ ): TaskTypes.CustomTask | undefined {
1613
+ if (!external) {
1614
+ return undefined;
1615
+ }
1616
+ let type = external.type;
1617
+ if (type === undefined || type === null) {
1618
+ type = TaskTypes.CUSTOMIZED_TASK_TYPE;
1619
+ }
1620
+ if (type !== TaskTypes.CUSTOMIZED_TASK_TYPE && type !== 'shell' && type !== 'process') {
1621
+ context.problemReporter.error(
1622
+ formatLocalize(
1623
+ 'ConfigurationParser.notCustom',
1624
+ 'Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n',
1625
+ JSON.stringify(external, null, 4),
1626
+ ),
1627
+ );
1628
+ return undefined;
1629
+ }
1630
+ let taskName = external.taskName;
1631
+ if (isString(external.label) && context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0) {
1632
+ taskName = external.label;
1633
+ }
1634
+ if (!taskName) {
1635
+ context.problemReporter.error(
1636
+ formatLocalize(
1637
+ 'ConfigurationParser.noTaskName',
1638
+ 'Error: a task must provide a label property. The task will be ignored.\n{0}\n',
1639
+ JSON.stringify(external, null, 4),
1640
+ ),
1641
+ );
1642
+ return undefined;
1643
+ }
1644
+ const result: TaskTypes.CustomTask = new TaskTypes.CustomTask(
1645
+ context.uuidMap.getUUID(taskName),
1646
+ Object.assign({} as TaskTypes.WorkspaceTaskSource, source, {
1647
+ config: { index, element: external, file: '.vscode/json', workspaceFolder: context.workspaceFolder },
1648
+ }),
1649
+ taskName,
1650
+ TaskTypes.CUSTOMIZED_TASK_TYPE,
1651
+ undefined,
1652
+ false,
1653
+ RunOptions.fromConfiguration(external.runOptions),
1654
+ {
1655
+ name: taskName,
1656
+ identifier: taskName,
1657
+ },
1658
+ );
1659
+ const configuration = ConfigurationProperties.from(external, context, false);
1660
+ if (configuration) {
1661
+ result.configurationProperties = Object.assign(result.configurationProperties, configuration);
1662
+ }
1663
+ const supportLegacy = true; // context.schemaVersion === JsonSchemaVersion.V2_0_0;
1664
+ if (supportLegacy) {
1665
+ const legacy: LegacyTaskProperties = external as LegacyTaskProperties;
1666
+ if (result.configurationProperties.isBackground === undefined && legacy.isWatching !== undefined) {
1667
+ result.configurationProperties.isBackground = !!legacy.isWatching;
1668
+ }
1669
+ if (result.configurationProperties.group === undefined) {
1670
+ if (legacy.isBuildCommand === true) {
1671
+ result.configurationProperties.group = TaskTypes.TaskGroup.Build._id;
1672
+ } else if (legacy.isTestCommand === true) {
1673
+ result.configurationProperties.group = TaskTypes.TaskGroup.Test._id;
1674
+ }
1675
+ }
1676
+ }
1677
+ const command: TaskTypes.CommandConfiguration = CommandConfiguration.from(external, context)!;
1678
+ if (command) {
1679
+ result.command = command;
1680
+ }
1681
+ if (external.command !== undefined) {
1682
+ // if the task has its own command then we suppress the
1683
+ // task name by default.
1684
+ command.suppressTaskName = true;
1685
+ }
1686
+ return result;
1687
+ }
1688
+
1689
+ export function fillGlobals(task: TaskTypes.CustomTask, globals: Globals): void {
1690
+ // We only merge a command from a global definition if there is no dependsOn
1691
+ // or there is a dependsOn and a defined command.
1692
+ if (CommandConfiguration.hasCommand(task.command) || task.configurationProperties.dependsOn === undefined) {
1693
+ task.command = CommandConfiguration.fillGlobals(task.command, globals.command, task.configurationProperties.name);
1694
+ }
1695
+ if (task.configurationProperties.problemMatchers === undefined && globals.problemMatcher !== undefined) {
1696
+ task.configurationProperties.problemMatchers = deepClone(globals.problemMatcher);
1697
+ task.hasDefinedMatchers = true;
1698
+ }
1699
+ // promptOnClose is inferred from isBackground if available
1700
+ if (
1701
+ task.configurationProperties.promptOnClose === undefined &&
1702
+ task.configurationProperties.isBackground === undefined &&
1703
+ globals.promptOnClose !== undefined
1704
+ ) {
1705
+ task.configurationProperties.promptOnClose = globals.promptOnClose;
1706
+ }
1707
+ }
1708
+
1709
+ export function fillDefaults(task: TaskTypes.CustomTask, context: ParseContext): void {
1710
+ CommandConfiguration.fillDefaults(task.command, context);
1711
+ if (task.configurationProperties.promptOnClose === undefined) {
1712
+ task.configurationProperties.promptOnClose =
1713
+ task.configurationProperties.isBackground !== undefined ? !task.configurationProperties.isBackground : true;
1714
+ }
1715
+ if (task.configurationProperties.isBackground === undefined) {
1716
+ task.configurationProperties.isBackground = false;
1717
+ }
1718
+ if (task.configurationProperties.problemMatchers === undefined) {
1719
+ task.configurationProperties.problemMatchers = EMPTY_ARRAY;
1720
+ }
1721
+ if (task.configurationProperties.group !== undefined && task.configurationProperties.groupType === undefined) {
1722
+ task.configurationProperties.groupType = TaskTypes.GroupType.user;
1723
+ }
1724
+ }
1725
+
1726
+ export function createCustomTask(
1727
+ contributedTask: TaskTypes.ContributedTask,
1728
+ configuredProps: TaskTypes.ConfiguringTask | TaskTypes.CustomTask,
1729
+ ): TaskTypes.CustomTask {
1730
+ const result: TaskTypes.CustomTask = new TaskTypes.CustomTask(
1731
+ configuredProps._id,
1732
+ Object.assign({}, configuredProps._source, { customizes: contributedTask.defines }),
1733
+ configuredProps.configurationProperties.name || contributedTask._label,
1734
+ TaskTypes.CUSTOMIZED_TASK_TYPE,
1735
+ contributedTask.command,
1736
+ false,
1737
+ contributedTask.runOptions,
1738
+ {
1739
+ name: configuredProps.configurationProperties.name || contributedTask.configurationProperties.name,
1740
+ identifier:
1741
+ configuredProps.configurationProperties.identifier || contributedTask.configurationProperties.identifier,
1742
+ },
1743
+ );
1744
+ result.addTaskLoadMessages(configuredProps.taskLoadMessages);
1745
+ const resultConfigProps: TaskTypes.ConfigurationProperties = result.configurationProperties;
1746
+
1747
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'group');
1748
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'groupType');
1749
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'isBackground');
1750
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'dependsOn');
1751
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'problemMatchers');
1752
+ assignProperty(resultConfigProps, configuredProps.configurationProperties, 'promptOnClose');
1753
+ result.command.presentation = CommandConfiguration.PresentationOptions.assignProperties(
1754
+ result.command.presentation!,
1755
+ configuredProps.configurationProperties.presentation,
1756
+ )!;
1757
+ result.command.options = CommandOptions.assignProperties(
1758
+ result.command.options,
1759
+ configuredProps.configurationProperties.options,
1760
+ );
1761
+
1762
+ const contributedConfigProps: TaskTypes.ConfigurationProperties = contributedTask.configurationProperties;
1763
+ fillProperty(resultConfigProps, contributedConfigProps, 'group');
1764
+ fillProperty(resultConfigProps, contributedConfigProps, 'groupType');
1765
+ fillProperty(resultConfigProps, contributedConfigProps, 'isBackground');
1766
+ fillProperty(resultConfigProps, contributedConfigProps, 'dependsOn');
1767
+ fillProperty(resultConfigProps, contributedConfigProps, 'problemMatchers');
1768
+ fillProperty(resultConfigProps, contributedConfigProps, 'promptOnClose');
1769
+ result.command.presentation = CommandConfiguration.PresentationOptions.fillProperties(
1770
+ result.command.presentation!,
1771
+ contributedConfigProps.presentation,
1772
+ )!;
1773
+ result.command.options = CommandOptions.fillProperties(result.command.options, contributedConfigProps.options);
1774
+
1775
+ if (contributedTask.hasDefinedMatchers === true) {
1776
+ result.hasDefinedMatchers = true;
1777
+ }
1778
+
1779
+ return result;
1780
+ }
1781
+ }
1782
+
1783
+ interface TaskParseResult {
1784
+ custom: TaskTypes.CustomTask[];
1785
+ configured: TaskTypes.ConfiguringTask[];
1786
+ }
1787
+
1788
+ namespace TaskParser {
1789
+ function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask {
1790
+ const type = value.type;
1791
+ const customize = (value as any).customize;
1792
+ return (
1793
+ customize === undefined &&
1794
+ (type === undefined ||
1795
+ type === null ||
1796
+ type === TaskTypes.CUSTOMIZED_TASK_TYPE ||
1797
+ type === 'shell' ||
1798
+ type === 'process')
1799
+ );
1800
+ }
1801
+
1802
+ export function from(
1803
+ this: void,
1804
+ externals: Array<CustomTask | ConfiguringTask> | undefined,
1805
+ globals: Globals,
1806
+ context: ParseContext,
1807
+ taskDefinitionRegister,
1808
+ ): TaskParseResult {
1809
+ const result: TaskParseResult = { custom: [], configured: [] };
1810
+ if (!externals) {
1811
+ return result;
1812
+ }
1813
+ const defaultBuildTask: { task: TaskTypes.Task | undefined; rank: number } = { task: undefined, rank: -1 };
1814
+ const defaultTestTask: { task: TaskTypes.Task | undefined; rank: number } = { task: undefined, rank: -1 };
1815
+ // tslint:disable-next-line: variable-name
1816
+ const schema2_0_0: boolean = context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0;
1817
+ const baseLoadIssues = deepClone(context.taskLoadIssues);
1818
+ for (let index = 0; index < externals.length; index++) {
1819
+ const external = externals[index];
1820
+ if (isCustomTask(external)) {
1821
+ const customTask = CustomTask.from(external, context, index);
1822
+ if (customTask) {
1823
+ CustomTask.fillGlobals(customTask, globals);
1824
+ CustomTask.fillDefaults(customTask, context);
1825
+ if (schema2_0_0) {
1826
+ if (
1827
+ (customTask.command === undefined || customTask.command.name === undefined) &&
1828
+ (customTask.configurationProperties.dependsOn === undefined ||
1829
+ customTask.configurationProperties.dependsOn.length === 0)
1830
+ ) {
1831
+ context.problemReporter.error(
1832
+ formatLocalize(
1833
+ 'taskConfiguration.noCommandOrDependsOn',
1834
+ "Error: the task '{0}' neither specifies a command nor a dependsOn property. The task will be ignored. Its definition is:\n{1}",
1835
+ customTask.configurationProperties.name,
1836
+ JSON.stringify(external, undefined, 4),
1837
+ ),
1838
+ );
1839
+ continue;
1840
+ }
1841
+ } else {
1842
+ if (customTask.command === undefined || customTask.command.name === undefined) {
1843
+ context.problemReporter.warn(
1844
+ formatLocalize(
1845
+ 'taskConfiguration.noCommand',
1846
+ "Error: the task '{0}' doesn't define a command. The task will be ignored. Its definition is:\n{1}",
1847
+ customTask.configurationProperties.name,
1848
+ JSON.stringify(external, undefined, 4),
1849
+ ),
1850
+ );
1851
+ continue;
1852
+ }
1853
+ }
1854
+ if (customTask.configurationProperties.group === TaskTypes.TaskGroup.Build._id && defaultBuildTask.rank < 2) {
1855
+ defaultBuildTask.task = customTask;
1856
+ defaultBuildTask.rank = 2;
1857
+ } else if (
1858
+ customTask.configurationProperties.group === TaskTypes.TaskGroup.Test._id &&
1859
+ defaultTestTask.rank < 2
1860
+ ) {
1861
+ defaultTestTask.task = customTask;
1862
+ defaultTestTask.rank = 2;
1863
+ } else if (customTask.configurationProperties.name === 'build' && defaultBuildTask.rank < 1) {
1864
+ defaultBuildTask.task = customTask;
1865
+ defaultBuildTask.rank = 1;
1866
+ } else if (customTask.configurationProperties.name === 'test' && defaultTestTask.rank < 1) {
1867
+ defaultTestTask.task = customTask;
1868
+ defaultTestTask.rank = 1;
1869
+ }
1870
+ customTask.addTaskLoadMessages(context.taskLoadIssues);
1871
+ result.custom.push(customTask);
1872
+ }
1873
+ } else {
1874
+ const configuredTask = ConfiguringTask.from(external, context, index, taskDefinitionRegister);
1875
+ if (configuredTask) {
1876
+ configuredTask.addTaskLoadMessages(context.taskLoadIssues);
1877
+ result.configured.push(configuredTask);
1878
+ }
1879
+ }
1880
+ context.taskLoadIssues = deepClone(baseLoadIssues);
1881
+ }
1882
+ if (defaultBuildTask.rank > -1 && defaultBuildTask.rank < 2 && defaultBuildTask.task) {
1883
+ defaultBuildTask.task.configurationProperties.group = TaskTypes.TaskGroup.Build._id;
1884
+ defaultBuildTask.task.configurationProperties.groupType = TaskTypes.GroupType.user;
1885
+ } else if (defaultTestTask.rank > -1 && defaultTestTask.rank < 2 && defaultTestTask.task) {
1886
+ defaultTestTask.task.configurationProperties.group = TaskTypes.TaskGroup.Test._id;
1887
+ defaultTestTask.task.configurationProperties.groupType = TaskTypes.GroupType.user;
1888
+ }
1889
+ return result;
1890
+ }
1891
+
1892
+ export function assignTasks(target: TaskTypes.CustomTask[], source: TaskTypes.CustomTask[]): TaskTypes.CustomTask[] {
1893
+ if (source === undefined || source.length === 0) {
1894
+ return target;
1895
+ }
1896
+ if (target === undefined || target.length === 0) {
1897
+ return source;
1898
+ }
1899
+
1900
+ if (source) {
1901
+ // Tasks are keyed by ID but we need to merge by name
1902
+ const map: IStringDictionary<TaskTypes.CustomTask> = Object.create(null);
1903
+ target.forEach((task) => {
1904
+ map[task.configurationProperties.name!] = task;
1905
+ });
1906
+
1907
+ source.forEach((task) => {
1908
+ map[task.configurationProperties.name!] = task;
1909
+ });
1910
+ const newTarget: TaskTypes.CustomTask[] = [];
1911
+ target.forEach((task) => {
1912
+ newTarget.push(map[task.configurationProperties.name!]);
1913
+ delete map[task.configurationProperties.name!];
1914
+ });
1915
+ Object.keys(map).forEach((key) => newTarget.push(map[key]));
1916
+ target = newTarget;
1917
+ }
1918
+ return target;
1919
+ }
1920
+ }
1921
+
1922
+ interface Globals {
1923
+ command?: TaskTypes.CommandConfiguration;
1924
+ problemMatcher?: ProblemMatcher[];
1925
+ promptOnClose?: boolean;
1926
+ suppressTaskName?: boolean;
1927
+ }
1928
+
1929
+ namespace Globals {
1930
+ export function from(config: ExternalTaskRunnerConfiguration, context: ParseContext): Globals {
1931
+ let result = fromBase(config, context);
1932
+ let osGlobals: Globals | undefined;
1933
+ if (config.windows && context.platform === Platform.Windows) {
1934
+ osGlobals = fromBase(config.windows, context);
1935
+ } else if (config.osx && context.platform === Platform.Mac) {
1936
+ osGlobals = fromBase(config.osx, context);
1937
+ } else if (config.linux && context.platform === Platform.Linux) {
1938
+ osGlobals = fromBase(config.linux, context);
1939
+ }
1940
+ if (osGlobals) {
1941
+ result = Globals.assignProperties(result, osGlobals);
1942
+ }
1943
+ const command = CommandConfiguration.from(config, context);
1944
+ if (command) {
1945
+ result.command = command;
1946
+ }
1947
+ Globals.fillDefaults(result, context);
1948
+ Globals.freeze(result);
1949
+ return result;
1950
+ }
1951
+
1952
+ export function fromBase(this: void, config: BaseTaskRunnerConfiguration, context: ParseContext): Globals {
1953
+ const result: Globals = {};
1954
+ if (config.suppressTaskName !== undefined) {
1955
+ result.suppressTaskName = !!config.suppressTaskName;
1956
+ }
1957
+ if (config.promptOnClose !== undefined) {
1958
+ result.promptOnClose = !!config.promptOnClose;
1959
+ }
1960
+ if (config.problemMatcher) {
1961
+ result.problemMatcher = ProblemMatcherConverter.from(config.problemMatcher, context);
1962
+ }
1963
+ return result;
1964
+ }
1965
+
1966
+ export function isEmpty(value: Globals): boolean {
1967
+ return (
1968
+ !value ||
1969
+ (value.command === undefined && value.promptOnClose === undefined && value.suppressTaskName === undefined)
1970
+ );
1971
+ }
1972
+
1973
+ export function assignProperties(target: Globals, source: Globals): Globals {
1974
+ if (isEmpty(source)) {
1975
+ return target;
1976
+ }
1977
+ if (isEmpty(target)) {
1978
+ return source;
1979
+ }
1980
+ assignProperty(target, source, 'promptOnClose');
1981
+ assignProperty(target, source, 'suppressTaskName');
1982
+ return target;
1983
+ }
1984
+
1985
+ export function fillDefaults(value: Globals, context: ParseContext): void {
1986
+ if (!value) {
1987
+ return;
1988
+ }
1989
+ CommandConfiguration.fillDefaults(value.command, context);
1990
+ if (value.suppressTaskName === undefined) {
1991
+ value.suppressTaskName = context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0;
1992
+ }
1993
+ if (value.promptOnClose === undefined) {
1994
+ value.promptOnClose = true;
1995
+ }
1996
+ }
1997
+
1998
+ export function freeze(value: Globals): void {
1999
+ Object.freeze(value);
2000
+ if (value.command) {
2001
+ CommandConfiguration.freeze(value.command);
2002
+ }
2003
+ }
2004
+ }
2005
+
2006
+ export namespace ExecutionEngine {
2007
+ export function from(config: ExternalTaskRunnerConfiguration): TaskTypes.ExecutionEngine {
2008
+ const runner = config.runner || config._runner;
2009
+ let result: TaskTypes.ExecutionEngine | undefined;
2010
+ if (runner) {
2011
+ switch (runner) {
2012
+ case 'terminal':
2013
+ result = TaskTypes.ExecutionEngine.Terminal;
2014
+ break;
2015
+ case 'process':
2016
+ result = TaskTypes.ExecutionEngine.Process;
2017
+ break;
2018
+ }
2019
+ }
2020
+ const schemaVersion = JsonSchemaVersion.from(config);
2021
+ if (schemaVersion === TaskTypes.JsonSchemaVersion.V0_1_0) {
2022
+ return result || TaskTypes.ExecutionEngine.Process;
2023
+ } else if (schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0) {
2024
+ return TaskTypes.ExecutionEngine.Terminal;
2025
+ } else {
2026
+ throw new Error("Shouldn't happen.");
2027
+ }
2028
+ }
2029
+ }
2030
+
2031
+ export namespace JsonSchemaVersion {
2032
+ const _default: TaskTypes.JsonSchemaVersion = TaskTypes.JsonSchemaVersion.V2_0_0;
2033
+
2034
+ export function from(config: ExternalTaskRunnerConfiguration): TaskTypes.JsonSchemaVersion {
2035
+ const version = config.version;
2036
+ if (!version) {
2037
+ return _default;
2038
+ }
2039
+ switch (version) {
2040
+ case '0.1.0':
2041
+ return TaskTypes.JsonSchemaVersion.V0_1_0;
2042
+ case '2.0.0':
2043
+ return TaskTypes.JsonSchemaVersion.V2_0_0;
2044
+ default:
2045
+ return _default;
2046
+ }
2047
+ }
2048
+ }
2049
+
2050
+ export interface ParseResult {
2051
+ validationStatus: ValidationStatus;
2052
+ custom: TaskTypes.CustomTask[];
2053
+ configured: TaskTypes.ConfiguringTask[];
2054
+ engine: TaskTypes.ExecutionEngine;
2055
+ }
2056
+
2057
+ // tslint:disable-next-line: no-empty-interface
2058
+ export type IProblemReporter = IProblemReporterBase;
2059
+
2060
+ class UUIDMap {
2061
+ private last: IStringDictionary<string | string[]> | undefined;
2062
+ private current: IStringDictionary<string | string[]>;
2063
+
2064
+ constructor(other?: UUIDMap) {
2065
+ this.current = Object.create(null);
2066
+ if (other) {
2067
+ for (const key of Object.keys(other.current)) {
2068
+ const value = other.current[key];
2069
+ if (Array.isArray(value)) {
2070
+ this.current[key] = value.slice();
2071
+ } else {
2072
+ this.current[key] = value;
2073
+ }
2074
+ }
2075
+ }
2076
+ }
2077
+
2078
+ public start(): void {
2079
+ this.last = this.current;
2080
+ this.current = Object.create(null);
2081
+ }
2082
+
2083
+ public getUUID(identifier: string): string {
2084
+ const lastValue = this.last ? this.last[identifier] : undefined;
2085
+ let result: string | undefined;
2086
+ if (lastValue !== undefined) {
2087
+ if (Array.isArray(lastValue)) {
2088
+ result = lastValue.shift();
2089
+ if (lastValue.length === 0) {
2090
+ delete this.last![identifier];
2091
+ }
2092
+ } else {
2093
+ result = lastValue;
2094
+ delete this.last![identifier];
2095
+ }
2096
+ }
2097
+ if (result === undefined) {
2098
+ result = uuid();
2099
+ }
2100
+ const currentValue = this.current[identifier];
2101
+ if (currentValue === undefined) {
2102
+ this.current[identifier] = result;
2103
+ } else {
2104
+ if (Array.isArray(currentValue)) {
2105
+ currentValue.push(result);
2106
+ } else {
2107
+ const arrayValue: string[] = [currentValue];
2108
+ arrayValue.push(result);
2109
+ this.current[identifier] = arrayValue;
2110
+ }
2111
+ }
2112
+ return result;
2113
+ }
2114
+
2115
+ public finish(): void {
2116
+ this.last = undefined;
2117
+ }
2118
+ }
2119
+
2120
+ class ConfigurationParser {
2121
+ private workspaceFolder: IWorkspaceFolder;
2122
+ private problemReporter: IProblemReporter;
2123
+ private uuidMap: UUIDMap;
2124
+ private platform: Platform;
2125
+
2126
+ constructor(
2127
+ workspaceFolder: IWorkspaceFolder,
2128
+ platform: Platform,
2129
+ problemReporter: IProblemReporter,
2130
+ uuidMap: UUIDMap,
2131
+ private createTaskIdentifier: createTaskIdentifierFn,
2132
+ private getProblemMatcher: getProblemMatcherFn,
2133
+ private getProblemPattern: getProblemPatternFn,
2134
+ ) {
2135
+ this.workspaceFolder = workspaceFolder;
2136
+ this.platform = platform;
2137
+ this.problemReporter = problemReporter;
2138
+ this.uuidMap = uuidMap;
2139
+ }
2140
+
2141
+ public run(fileConfig: ExternalTaskRunnerConfiguration, taskDefinitionRegister): ParseResult {
2142
+ const engine = ExecutionEngine.from(fileConfig);
2143
+ const schemaVersion = JsonSchemaVersion.from(fileConfig);
2144
+ const context: ParseContext = {
2145
+ workspaceFolder: this.workspaceFolder,
2146
+ problemReporter: this.problemReporter,
2147
+ uuidMap: this.uuidMap,
2148
+ namedProblemMatchers: {},
2149
+ engine,
2150
+ schemaVersion,
2151
+ platform: this.platform,
2152
+ taskLoadIssues: [],
2153
+ createTaskIdentifier: this.createTaskIdentifier,
2154
+ getProblemMatcher: this.getProblemMatcher,
2155
+ getProblemPattern: this.getProblemPattern,
2156
+ };
2157
+ const taskParseResult = this.createTaskRunnerConfiguration(fileConfig, context, taskDefinitionRegister);
2158
+ return {
2159
+ validationStatus: this.problemReporter.status,
2160
+ custom: taskParseResult.custom,
2161
+ configured: taskParseResult.configured,
2162
+ engine,
2163
+ };
2164
+ }
2165
+
2166
+ private createTaskRunnerConfiguration(
2167
+ fileConfig: ExternalTaskRunnerConfiguration,
2168
+ context: ParseContext,
2169
+ taskDefinitionRegister,
2170
+ ): TaskParseResult {
2171
+ const globals = Globals.from(fileConfig, context);
2172
+ if (this.problemReporter.status.isFatal()) {
2173
+ return { custom: [], configured: [] };
2174
+ }
2175
+ context.namedProblemMatchers = ProblemMatcherConverter.namedFrom(fileConfig.declares, context);
2176
+ let globalTasks: TaskTypes.CustomTask[] | undefined;
2177
+ let externalGlobalTasks: Array<ConfiguringTask | CustomTask> | undefined;
2178
+ if (fileConfig.windows && context.platform === Platform.Windows) {
2179
+ globalTasks = TaskParser.from(fileConfig.windows.tasks, globals, context, taskDefinitionRegister).custom;
2180
+ externalGlobalTasks = fileConfig.windows.tasks;
2181
+ } else if (fileConfig.osx && context.platform === Platform.Mac) {
2182
+ globalTasks = TaskParser.from(fileConfig.osx.tasks, globals, context, taskDefinitionRegister).custom;
2183
+ externalGlobalTasks = fileConfig.osx.tasks;
2184
+ } else if (fileConfig.linux && context.platform === Platform.Linux) {
2185
+ globalTasks = TaskParser.from(fileConfig.linux.tasks, globals, context, taskDefinitionRegister).custom;
2186
+ externalGlobalTasks = fileConfig.linux.tasks;
2187
+ }
2188
+ if (
2189
+ context.schemaVersion === TaskTypes.JsonSchemaVersion.V2_0_0 &&
2190
+ globalTasks &&
2191
+ globalTasks.length > 0 &&
2192
+ externalGlobalTasks &&
2193
+ externalGlobalTasks.length > 0
2194
+ ) {
2195
+ const taskContent: string[] = [];
2196
+ for (const task of externalGlobalTasks) {
2197
+ taskContent.push(JSON.stringify(task, null, 4));
2198
+ }
2199
+ context.problemReporter.error(
2200
+ formatLocalize(
2201
+ 'TaskParse.noOsSpecificGlobalTasks',
2202
+ "Task version 2.0.0 doesn't support global OS specific Convert them to a task with a OS specific command. Affected tasks are:\n{0}",
2203
+ taskContent.join('\n'),
2204
+ ),
2205
+ );
2206
+ }
2207
+
2208
+ let result: TaskParseResult = { custom: [], configured: [] };
2209
+ if (fileConfig.tasks) {
2210
+ result = TaskParser.from(fileConfig.tasks, globals, context, taskDefinitionRegister);
2211
+ }
2212
+ if (globalTasks) {
2213
+ result.custom = TaskParser.assignTasks(result.custom, globalTasks);
2214
+ }
2215
+
2216
+ if ((!result.custom || result.custom.length === 0) && globals.command && globals.command.name) {
2217
+ const matchers: ProblemMatcher[] = ProblemMatcherConverter.from(fileConfig.problemMatcher, context);
2218
+ const isBackground = fileConfig.isBackground
2219
+ ? !!fileConfig.isBackground
2220
+ : fileConfig.isWatching
2221
+ ? !!fileConfig.isWatching
2222
+ : undefined;
2223
+ const name = TaskTypes.CommandString.value(globals.command.name);
2224
+ const task: TaskTypes.CustomTask = new TaskTypes.CustomTask(
2225
+ context.uuidMap.getUUID(name),
2226
+ Object.assign({} as TaskTypes.WorkspaceTaskSource, source, {
2227
+ config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder },
2228
+ }),
2229
+ name,
2230
+ TaskTypes.CUSTOMIZED_TASK_TYPE,
2231
+ {
2232
+ name: undefined,
2233
+ runtime: undefined,
2234
+ presentation: undefined,
2235
+ suppressTaskName: true,
2236
+ },
2237
+ false,
2238
+ { reevaluateOnRerun: true },
2239
+ {
2240
+ name,
2241
+ identifier: name,
2242
+ group: TaskTypes.TaskGroup.Build._id,
2243
+ isBackground,
2244
+ problemMatchers: matchers,
2245
+ },
2246
+ );
2247
+ const value = GroupKind.from(fileConfig.group);
2248
+ if (value) {
2249
+ task.configurationProperties.group = value[0];
2250
+ task.configurationProperties.groupType = value[1];
2251
+ } else if (fileConfig.group === 'none') {
2252
+ task.configurationProperties.group = undefined;
2253
+ }
2254
+ CustomTask.fillGlobals(task, globals);
2255
+ CustomTask.fillDefaults(task, context);
2256
+ result.custom = [task];
2257
+ }
2258
+
2259
+ result.custom = result.custom || [];
2260
+ result.configured = result.configured || [];
2261
+ return result;
2262
+ }
2263
+ }
2264
+
2265
+ const uuidMaps: Map<string, UUIDMap> = new Map();
2266
+
2267
+ export function parse(
2268
+ workspaceFolder: IWorkspaceFolder,
2269
+ platform: Platform,
2270
+ configuration: ExternalTaskRunnerConfiguration,
2271
+ logger: IProblemReporter,
2272
+ taskDefinitionRegister: ITaskDefinitionRegistry,
2273
+ problemMatcherRegister: IProblemMatcherRegistry,
2274
+ problemPatternRegister: IProblemPatternRegistry,
2275
+ ): ParseResult {
2276
+ let uuidMap = uuidMaps.get(workspaceFolder.uri.toString());
2277
+ if (!uuidMap) {
2278
+ uuidMap = new UUIDMap();
2279
+ uuidMaps.set(workspaceFolder.uri.toString(), uuidMap);
2280
+ }
2281
+ try {
2282
+ uuidMap.start();
2283
+ return new ConfigurationParser(
2284
+ workspaceFolder,
2285
+ platform,
2286
+ logger,
2287
+ uuidMap,
2288
+ taskDefinitionRegister.createTaskIdentifier,
2289
+ problemMatcherRegister.get,
2290
+ problemPatternRegister.get,
2291
+ ).run(configuration, taskDefinitionRegister);
2292
+ } finally {
2293
+ uuidMap.finish();
2294
+ }
2295
+ }
2296
+
2297
+ export function createCustomTask(
2298
+ contributedTask: TaskTypes.ContributedTask,
2299
+ configuredProps: TaskTypes.ConfiguringTask | TaskTypes.CustomTask,
2300
+ ): TaskTypes.CustomTask {
2301
+ return CustomTask.createCustomTask(contributedTask, configuredProps);
2302
+ }