@theia/plugin-utils 1.75.0-next.28

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 (122) hide show
  1. package/README.md +30 -0
  2. package/lib/common/constants.d.ts +12 -0
  3. package/lib/common/constants.d.ts.map +1 -0
  4. package/lib/common/constants.js +30 -0
  5. package/lib/common/constants.js.map +1 -0
  6. package/lib/common/constants.spec.d.ts +2 -0
  7. package/lib/common/constants.spec.d.ts.map +1 -0
  8. package/lib/common/constants.spec.js +33 -0
  9. package/lib/common/constants.spec.js.map +1 -0
  10. package/lib/common/contribution-types.d.ts +648 -0
  11. package/lib/common/contribution-types.d.ts.map +1 -0
  12. package/lib/common/contribution-types.js +30 -0
  13. package/lib/common/contribution-types.js.map +1 -0
  14. package/lib/common/manifest-types.d.ts +105 -0
  15. package/lib/common/manifest-types.d.ts.map +1 -0
  16. package/lib/common/manifest-types.js +31 -0
  17. package/lib/common/manifest-types.js.map +1 -0
  18. package/lib/common/manifest-types.spec.d.ts +2 -0
  19. package/lib/common/manifest-types.spec.d.ts.map +1 -0
  20. package/lib/common/manifest-types.spec.js +35 -0
  21. package/lib/common/manifest-types.spec.js.map +1 -0
  22. package/lib/common/package-nls.d.ts +24 -0
  23. package/lib/common/package-nls.d.ts.map +1 -0
  24. package/lib/common/package-nls.js +82 -0
  25. package/lib/common/package-nls.js.map +1 -0
  26. package/lib/common/package-nls.spec.d.ts +2 -0
  27. package/lib/common/package-nls.spec.d.ts.map +1 -0
  28. package/lib/common/package-nls.spec.js +98 -0
  29. package/lib/common/package-nls.spec.js.map +1 -0
  30. package/lib/common/plugin-activation-events.d.ts +7 -0
  31. package/lib/common/plugin-activation-events.d.ts.map +1 -0
  32. package/lib/common/plugin-activation-events.js +95 -0
  33. package/lib/common/plugin-activation-events.js.map +1 -0
  34. package/lib/common/plugin-activation-events.spec.d.ts +2 -0
  35. package/lib/common/plugin-activation-events.spec.d.ts.map +1 -0
  36. package/lib/common/plugin-activation-events.spec.js +89 -0
  37. package/lib/common/plugin-activation-events.spec.js.map +1 -0
  38. package/lib/common/plugin-manifest.d.ts +10 -0
  39. package/lib/common/plugin-manifest.d.ts.map +1 -0
  40. package/lib/common/plugin-manifest.js +35 -0
  41. package/lib/common/plugin-manifest.js.map +1 -0
  42. package/lib/common/plugin-manifest.spec.d.ts +2 -0
  43. package/lib/common/plugin-manifest.spec.d.ts.map +1 -0
  44. package/lib/common/plugin-manifest.spec.js +83 -0
  45. package/lib/common/plugin-manifest.spec.js.map +1 -0
  46. package/lib/common/plugin-model.d.ts +33 -0
  47. package/lib/common/plugin-model.d.ts.map +1 -0
  48. package/lib/common/plugin-model.js +166 -0
  49. package/lib/common/plugin-model.js.map +1 -0
  50. package/lib/common/plugin-model.spec.d.ts +2 -0
  51. package/lib/common/plugin-model.spec.d.ts.map +1 -0
  52. package/lib/common/plugin-model.spec.js +204 -0
  53. package/lib/common/plugin-model.spec.js.map +1 -0
  54. package/lib/common/protocol-shims.d.ts +128 -0
  55. package/lib/common/protocol-shims.d.ts.map +1 -0
  56. package/lib/common/protocol-shims.js +29 -0
  57. package/lib/common/protocol-shims.js.map +1 -0
  58. package/lib/common/test-helpers.d.ts +5 -0
  59. package/lib/common/test-helpers.d.ts.map +1 -0
  60. package/lib/common/test-helpers.js +41 -0
  61. package/lib/common/test-helpers.js.map +1 -0
  62. package/lib/common/utils.d.ts +20 -0
  63. package/lib/common/utils.d.ts.map +1 -0
  64. package/lib/common/utils.js +74 -0
  65. package/lib/common/utils.js.map +1 -0
  66. package/lib/common/utils.spec.d.ts +2 -0
  67. package/lib/common/utils.spec.d.ts.map +1 -0
  68. package/lib/common/utils.spec.js +77 -0
  69. package/lib/common/utils.spec.js.map +1 -0
  70. package/lib/node/normalize-contributions.d.ts +54 -0
  71. package/lib/node/normalize-contributions.d.ts.map +1 -0
  72. package/lib/node/normalize-contributions.js +985 -0
  73. package/lib/node/normalize-contributions.js.map +1 -0
  74. package/lib/node/normalize-contributions.spec.d.ts +2 -0
  75. package/lib/node/normalize-contributions.spec.d.ts.map +1 -0
  76. package/lib/node/normalize-contributions.spec.js +850 -0
  77. package/lib/node/normalize-contributions.spec.js.map +1 -0
  78. package/lib/node/package-nls.d.ts +3 -0
  79. package/lib/node/package-nls.d.ts.map +1 -0
  80. package/lib/node/package-nls.js +45 -0
  81. package/lib/node/package-nls.js.map +1 -0
  82. package/lib/node/plugin-manifest.d.ts +6 -0
  83. package/lib/node/plugin-manifest.d.ts.map +1 -0
  84. package/lib/node/plugin-manifest.js +26 -0
  85. package/lib/node/plugin-manifest.js.map +1 -0
  86. package/lib/node/plugin-model.d.ts +5 -0
  87. package/lib/node/plugin-model.d.ts.map +1 -0
  88. package/lib/node/plugin-model.js +36 -0
  89. package/lib/node/plugin-model.js.map +1 -0
  90. package/lib/node/read-grammars.d.ts +10 -0
  91. package/lib/node/read-grammars.d.ts.map +1 -0
  92. package/lib/node/read-grammars.js +83 -0
  93. package/lib/node/read-grammars.js.map +1 -0
  94. package/lib/node/read-grammars.spec.d.ts +2 -0
  95. package/lib/node/read-grammars.spec.d.ts.map +1 -0
  96. package/lib/node/read-grammars.spec.js +109 -0
  97. package/lib/node/read-grammars.spec.js.map +1 -0
  98. package/package.json +42 -0
  99. package/src/common/constants.spec.ts +34 -0
  100. package/src/common/constants.ts +35 -0
  101. package/src/common/contribution-types.ts +677 -0
  102. package/src/common/manifest-types.spec.ts +36 -0
  103. package/src/common/manifest-types.ts +126 -0
  104. package/src/common/package-nls.spec.ts +122 -0
  105. package/src/common/package-nls.ts +95 -0
  106. package/src/common/plugin-activation-events.spec.ts +104 -0
  107. package/src/common/plugin-activation-events.ts +102 -0
  108. package/src/common/plugin-manifest.spec.ts +94 -0
  109. package/src/common/plugin-manifest.ts +44 -0
  110. package/src/common/plugin-model.spec.ts +246 -0
  111. package/src/common/plugin-model.ts +194 -0
  112. package/src/common/protocol-shims.ts +148 -0
  113. package/src/common/test-helpers.ts +44 -0
  114. package/src/common/utils.spec.ts +86 -0
  115. package/src/common/utils.ts +81 -0
  116. package/src/node/normalize-contributions.spec.ts +931 -0
  117. package/src/node/normalize-contributions.ts +1098 -0
  118. package/src/node/package-nls.ts +45 -0
  119. package/src/node/plugin-manifest.ts +30 -0
  120. package/src/node/plugin-model.ts +33 -0
  121. package/src/node/read-grammars.spec.ts +128 -0
  122. package/src/node/read-grammars.ts +97 -0
@@ -0,0 +1,1098 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2015-2021 Red Hat, Inc.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ /* eslint-disable @theia/localization-check */
18
+
19
+ import { promises as fs } from 'fs';
20
+ import * as path from 'path';
21
+ import * as jsoncparser from 'jsonc-parser';
22
+ import { deepClone, isColorDefaults, isENOENT, isObject, isStringArray } from '../common/utils';
23
+ import {
24
+ ICON_NAME_SEGMENT,
25
+ type ColorDefinition,
26
+ type IJSONSchema,
27
+ type JsonType,
28
+ type JSONValue,
29
+ type PreferenceDataProperty,
30
+ type PreferenceSchema,
31
+ PreferenceScope,
32
+ type TaskDefinition,
33
+ } from '../common/protocol-shims';
34
+ import {
35
+ CustomEditorPriority,
36
+ type AutoClosingPair,
37
+ type AutoClosingPairConditional,
38
+ type CharacterPair,
39
+ type CommentRule,
40
+ type FoldingRules,
41
+ type IconUrl,
42
+ type IConfigurationNode,
43
+ type IndentationRules,
44
+ type NormalizeContributionsContext,
45
+ type NormalizedCommand,
46
+ type NormalizedCustomEditor,
47
+ type NormalizedIcon,
48
+ type NormalizedIconTheme,
49
+ type NormalizedKeybinding,
50
+ type NormalizedLanguage,
51
+ type NormalizedLanguageConfiguration,
52
+ type NormalizedLocalization,
53
+ type NormalizedPluginContribution,
54
+ type NormalizedSnippet,
55
+ type NormalizedSubmenu,
56
+ type NormalizedTerminalProfile,
57
+ type NormalizedTheme,
58
+ type NormalizedTranslation,
59
+ type NormalizedViewContainer,
60
+ type NormalizedViewWelcome,
61
+ type PluginUiTheme,
62
+ type RawCommand,
63
+ type RawCustomEditor,
64
+ type RawDebugger,
65
+ type RawKeybinding,
66
+ type RawLanguage,
67
+ type RawLocalization,
68
+ type RawMenu,
69
+ type RawSubmenu,
70
+ type RawTaskDefinition,
71
+ type RawTranslation,
72
+ type RawView,
73
+ type RawViewContainer,
74
+ type RawViewWelcome,
75
+ type ViewsByLocation,
76
+ type WalkthroughContribution,
77
+ type WalkthroughStepContribution,
78
+ type WalkthroughStepMedia,
79
+ type PluginPackageWalkthroughStepMedia,
80
+ } from '../common/contribution-types';
81
+ import { rawContributes, type PluginManifest } from '../common/manifest-types';
82
+ import { UNPUBLISHED } from '../common/constants';
83
+
84
+ function isPluginUiTheme(value: unknown): value is PluginUiTheme {
85
+ return value === 'vs' || value === 'vs-dark' || value === 'hc-black';
86
+ }
87
+
88
+ export const colorIdPattern = '^\\w+[.\\w+]*$';
89
+ export const iconIdPattern = `^${ICON_NAME_SEGMENT}(-${ICON_NAME_SEGMENT})+$`;
90
+
91
+ /** Maps the view container locations used by VS Code extensions to the Theia shell locations. */
92
+ export const VIEW_CONTAINER_LOCATION_ALIASES: Record<string, string> = {
93
+ activitybar: 'left',
94
+ panel: 'bottom',
95
+ secondarySidebar: 'right',
96
+ auxiliarybar: 'right'
97
+ };
98
+
99
+ /**
100
+ * Mirrors VS Code's `ConfigurationRegistry.getDefaultValue(type)`: plugins expect typed-but-defaultless properties to read as a type-based default.
101
+ */
102
+ export function deriveDefaultForType(type: JsonType | JsonType[] | undefined): JSONValue {
103
+ const t = Array.isArray(type) ? type[0] : type;
104
+ switch (t) {
105
+ case 'boolean': return false;
106
+ case 'integer':
107
+ case 'number': return 0;
108
+ case 'string': return '';
109
+ case 'array': return [];
110
+ case 'object': return {};
111
+ // eslint-disable-next-line no-null/no-null
112
+ default: return null;
113
+ }
114
+ }
115
+
116
+ export function getScope(monacoScope: string | undefined): { scope: PreferenceScope | undefined; overridable: boolean } {
117
+ switch (monacoScope) {
118
+ case 'machine-overridable':
119
+ case 'window':
120
+ case 'resource':
121
+ return { scope: PreferenceScope.Folder, overridable: false };
122
+ case 'language-overridable':
123
+ return { scope: PreferenceScope.Folder, overridable: true };
124
+ case 'application':
125
+ case 'machine':
126
+ return { scope: PreferenceScope.User, overridable: false };
127
+ }
128
+ return { scope: undefined, overridable: false };
129
+ }
130
+
131
+ function getFileExtension(filePath: string): string {
132
+ const index = filePath.lastIndexOf('.');
133
+ return index === -1 ? '' : filePath.substring(index + 1);
134
+ }
135
+
136
+ function isCharacterPair(something: unknown): something is CharacterPair {
137
+ return isStringArray(something) && something.length === 2;
138
+ }
139
+
140
+ export function isRawLocalization(entry: unknown): entry is RawLocalization {
141
+ return isObject(entry) && typeof entry.languageId === 'string';
142
+ }
143
+
144
+ function toCustomEditorPriority(priority: string | undefined): CustomEditorPriority {
145
+ if (priority === CustomEditorPriority.option || priority === CustomEditorPriority.builtin) {
146
+ return priority;
147
+ }
148
+ return CustomEditorPriority.default;
149
+ }
150
+
151
+ async function readJson(ctx: NormalizeContributionsContext, filePath: string): Promise<unknown | undefined> {
152
+ if (ctx.readJsonFile) {
153
+ return ctx.readJsonFile(filePath);
154
+ }
155
+
156
+ try {
157
+ const content = await fs.readFile(filePath, { encoding: 'utf8' });
158
+ return content ? jsoncparser.parse(content, undefined, { disallowComments: false }) : undefined;
159
+ } catch (e) {
160
+ if (!isENOENT(e)) {
161
+ throw e;
162
+ }
163
+ return undefined;
164
+ }
165
+ }
166
+
167
+ export async function normalizeContributions<TContrib extends object>(
168
+ ctx: NormalizeContributionsContext,
169
+ contributions: TContrib
170
+ ): Promise<TContrib> {
171
+ const output = contributions as TContrib & NormalizedPluginContribution;
172
+ const rawPlugin = ctx.plugin;
173
+ const contributes = rawContributes(rawPlugin);
174
+ const readConfigurationFn = ctx.readConfiguration ?? readConfiguration;
175
+ const readSubmenusFn = ctx.readSubmenus ?? ((submenus, plugin) => readSubmenus(ctx, submenus, plugin));
176
+ const readCustomEditorsFn = ctx.readCustomEditors ?? readCustomEditors;
177
+ const readViewsContainersFn = ctx.readViewsContainers ?? ((containers, plugin) => readViewsContainers(ctx, containers, plugin));
178
+ const readViewsFn = ctx.readViews ?? readViews;
179
+ const readViewsWelcomeFn = ctx.readViewsWelcome ?? readViewsWelcome;
180
+ const readCommandFn = ctx.readCommand ?? ((command, plugin) => readCommand(ctx, command, plugin));
181
+ const readMenusFn = ctx.readMenus ?? readMenus;
182
+ const readKeybindingFn = ctx.readKeybinding ?? readKeybinding;
183
+ const readDebuggersFn = ctx.readDebuggers ?? readDebuggers;
184
+ const readTaskDefinitionFn = ctx.readTaskDefinition ?? ((pluginName, definition) => readTaskDefinition(pluginName, definition, ctx));
185
+ const readSnippetsFn = ctx.readSnippets ?? (plugin => readSnippets(ctx, plugin));
186
+ const readThemesFn = ctx.readThemes ?? (plugin => readThemes(ctx, plugin));
187
+ const readIconsFn = ctx.readIcons ?? (plugin => readIcons(ctx, plugin));
188
+ const readIconThemesFn = ctx.readIconThemes ?? (plugin => readIconThemes(ctx, plugin));
189
+ const readColorsFn = ctx.readColors ?? (plugin => readColors(ctx, plugin));
190
+ const readTerminalsFn = ctx.readTerminals ?? readTerminals;
191
+ const readLocalizationsFn = ctx.readLocalizations ?? (plugin => readLocalizations(plugin, ctx));
192
+ const readWalkthroughsFn = ctx.readWalkthroughs ?? (plugin => readWalkthroughs(plugin, ctx));
193
+ const readLanguagesFn = ctx.readLanguages ?? ((languages, plugin) => readLanguages(ctx, languages, plugin));
194
+
195
+ try {
196
+ if (contributes.configuration) {
197
+ const configurations = Array.isArray(contributes.configuration) ? contributes.configuration : [contributes.configuration];
198
+ const hasMultipleConfigs = configurations.length > 1;
199
+ output.configuration = [];
200
+ for (const c of configurations) {
201
+ const config = readConfigurationFn(c as IConfigurationNode, rawPlugin.packagePath);
202
+ if (config) {
203
+ Object.values(config.properties).forEach(property => {
204
+ if (hasMultipleConfigs) {
205
+ property.owner = rawPlugin.displayName;
206
+ property.group = config.title;
207
+ } else {
208
+ property.owner = config.title;
209
+ }
210
+ });
211
+ output.configuration.push(config);
212
+ }
213
+ }
214
+ }
215
+ } catch (err) {
216
+ ctx.onError('configuration', err, contributes.configuration);
217
+ }
218
+
219
+ output.problemMatchers = contributes.problemMatchers;
220
+ output.problemPatterns = contributes.problemPatterns;
221
+ output.resourceLabelFormatters = contributes.resourceLabelFormatters;
222
+ output.authentication = contributes.authentication;
223
+ output.notebooks = contributes.notebooks;
224
+ output.notebookRenderer = contributes.notebookRenderer;
225
+ output.notebookPreload = contributes.notebookPreload;
226
+
227
+ const configurationDefaults = contributes.configurationDefaults;
228
+ output.configurationDefaults = isObject(configurationDefaults) ? configurationDefaults : undefined;
229
+
230
+ try {
231
+ if (contributes.submenus) {
232
+ output.submenus = readSubmenusFn(contributes.submenus, rawPlugin);
233
+ }
234
+ } catch (err) {
235
+ ctx.onError('submenus', err, contributes.submenus);
236
+ }
237
+
238
+ try {
239
+ if (contributes.customEditors) {
240
+ output.customEditors = readCustomEditorsFn(contributes.customEditors);
241
+ }
242
+ } catch (err) {
243
+ ctx.onError('customEditors', err, contributes.customEditors);
244
+ }
245
+
246
+ try {
247
+ if (contributes.viewsContainers) {
248
+ const viewsContainers: { [location: string]: NormalizedViewContainer[] } = {};
249
+ for (const location of Object.keys(contributes.viewsContainers)) {
250
+ const containers = readViewsContainersFn(contributes.viewsContainers[location], rawPlugin);
251
+ const loc = VIEW_CONTAINER_LOCATION_ALIASES[location] ?? location;
252
+ const existing = viewsContainers[loc];
253
+ viewsContainers[loc] = existing ? existing.concat(containers) : containers;
254
+ }
255
+ output.viewsContainers = viewsContainers;
256
+ }
257
+ } catch (err) {
258
+ ctx.onError('viewsContainers', err, contributes.viewsContainers);
259
+ }
260
+
261
+ try {
262
+ if (contributes.views) {
263
+ const views: { [location: string]: RawView[] } = {};
264
+ for (const location of Object.keys(contributes.views)) {
265
+ views[location] = readViewsFn(contributes.views[location]);
266
+ }
267
+ output.views = views;
268
+ }
269
+ } catch (err) {
270
+ ctx.onError('views', err, contributes.views);
271
+ }
272
+
273
+ try {
274
+ if (contributes.viewsWelcome) {
275
+ output.viewsWelcome = readViewsWelcomeFn(
276
+ contributes.viewsWelcome,
277
+ contributes.views
278
+ );
279
+ }
280
+ } catch (err) {
281
+ ctx.onError('viewsWelcome', err, contributes.viewsWelcome);
282
+ }
283
+
284
+ try {
285
+ const pluginCommands = contributes.commands;
286
+ if (pluginCommands) {
287
+ const commands = Array.isArray(pluginCommands) ? pluginCommands : [pluginCommands];
288
+ output.commands = commands.map(command => readCommandFn(command, rawPlugin));
289
+ }
290
+ } catch (err) {
291
+ ctx.onError('commands', err, contributes.commands);
292
+ }
293
+
294
+ try {
295
+ if (contributes.menus) {
296
+ const menus: { [location: string]: RawMenu[] } = {};
297
+ for (const location of Object.keys(contributes.menus)) {
298
+ menus[location] = readMenusFn(contributes.menus[location]);
299
+ }
300
+ output.menus = menus;
301
+ }
302
+ } catch (err) {
303
+ ctx.onError('menus', err, contributes.menus);
304
+ }
305
+
306
+ try {
307
+ if (contributes.keybindings) {
308
+ const rawKeybindings = Array.isArray(contributes.keybindings) ? contributes.keybindings : [contributes.keybindings];
309
+ output.keybindings = rawKeybindings.map(rawKeybinding => readKeybindingFn(rawKeybinding));
310
+ }
311
+ } catch (err) {
312
+ ctx.onError('keybindings', err, contributes.keybindings);
313
+ }
314
+
315
+ try {
316
+ if (contributes.debuggers) {
317
+ output.debuggers = readDebuggersFn(contributes.debuggers);
318
+ }
319
+ } catch (err) {
320
+ ctx.onError('debuggers', err, contributes.debuggers);
321
+ }
322
+
323
+ try {
324
+ if (contributes.taskDefinitions) {
325
+ output.taskDefinitions = contributes.taskDefinitions.map(definitionContribution =>
326
+ readTaskDefinitionFn(rawPlugin.name, definitionContribution)
327
+ );
328
+ }
329
+ } catch (err) {
330
+ ctx.onError('taskDefinitions', err, contributes.taskDefinitions);
331
+ }
332
+
333
+ try {
334
+ output.snippets = readSnippetsFn(rawPlugin);
335
+ } catch (err) {
336
+ ctx.onError('snippets', err, contributes.snippets);
337
+ }
338
+
339
+ try {
340
+ output.themes = readThemesFn(rawPlugin);
341
+ } catch (err) {
342
+ ctx.onError('themes', err, contributes.themes);
343
+ }
344
+
345
+ try {
346
+ output.icons = readIconsFn(rawPlugin);
347
+ } catch (err) {
348
+ ctx.onError('icons', err, contributes.icons);
349
+ }
350
+
351
+ try {
352
+ output.iconThemes = readIconThemesFn(rawPlugin);
353
+ } catch (err) {
354
+ ctx.onError('iconThemes', err, contributes.iconThemes);
355
+ }
356
+
357
+ try {
358
+ output.colors = readColorsFn(rawPlugin);
359
+ } catch (err) {
360
+ ctx.onError('colors', err, contributes.colors);
361
+ }
362
+
363
+ try {
364
+ output.terminalProfiles = readTerminalsFn(rawPlugin);
365
+ } catch (err) {
366
+ ctx.onError('terminals', err, contributes.terminal);
367
+ }
368
+
369
+ try {
370
+ output.localizations = readLocalizationsFn(rawPlugin);
371
+ } catch (err) {
372
+ ctx.onError('localizations', err, contributes.localizations);
373
+ }
374
+
375
+ try {
376
+ output.walkthroughs = readWalkthroughsFn(rawPlugin);
377
+ } catch (err) {
378
+ ctx.onError('walkthroughs', err, contributes.walkthroughs);
379
+ }
380
+
381
+ const [languagesResult, grammarsResult] = await Promise.allSettled([
382
+ contributes.languages ? readLanguagesFn(contributes.languages, rawPlugin) : undefined,
383
+ contributes.grammars && ctx.readGrammars
384
+ ? ctx.readGrammars(contributes.grammars, rawPlugin.packagePath)
385
+ : undefined
386
+ ]);
387
+
388
+ if (contributes.languages) {
389
+ if (languagesResult.status === 'fulfilled') {
390
+ output.languages = languagesResult.value;
391
+ } else {
392
+ ctx.onError('languages', languagesResult.reason, contributes.languages);
393
+ }
394
+ }
395
+
396
+ if (contributes.grammars) {
397
+ if (grammarsResult.status === 'fulfilled') {
398
+ output.grammars = grammarsResult.value;
399
+ } else {
400
+ ctx.onError('grammars', grammarsResult.reason, contributes.grammars);
401
+ }
402
+ }
403
+
404
+ // Drop keys with `undefined`
405
+ for (const key of Object.keys(output) as (keyof NormalizedPluginContribution)[]) {
406
+ if (output[key] === undefined) {
407
+ delete output[key];
408
+ }
409
+ }
410
+
411
+ return output;
412
+ }
413
+
414
+ export function readTerminals(pck: PluginManifest): NormalizedTerminalProfile[] | undefined {
415
+ const terminal = rawContributes(pck).terminal;
416
+ if (!terminal?.profiles) {
417
+ return undefined;
418
+ }
419
+ return terminal.profiles.filter((profile): profile is NormalizedTerminalProfile =>
420
+ !!(profile.id && profile.title)
421
+ );
422
+ }
423
+
424
+ export function readLocalizations(
425
+ pck: PluginManifest,
426
+ ctx?: NormalizeContributionsContext
427
+ ): NormalizedLocalization[] | undefined {
428
+ const localizations = rawContributes(pck).localizations;
429
+ if (!Array.isArray(localizations)) {
430
+ return undefined;
431
+ }
432
+ const readLocalizationFn = ctx?.readLocalization ?? ((entry, pluginPath) => readLocalization(entry, pluginPath, ctx));
433
+ return localizations
434
+ .filter(isRawLocalization)
435
+ .map(entry => readLocalizationFn(entry, pck.packagePath));
436
+ }
437
+
438
+ export function readLocalization(
439
+ { languageId, languageName, localizedLanguageName, translations, minimalTranslations }: RawLocalization,
440
+ pluginPath: string,
441
+ ctx?: Pick<NormalizeContributionsContext, 'readTranslation'>
442
+ ): NormalizedLocalization {
443
+ const readTranslationFn = ctx?.readTranslation ?? readTranslation;
444
+ const local: NormalizedLocalization = {
445
+ languageId,
446
+ languageName,
447
+ localizedLanguageName,
448
+ minimalTranslations,
449
+ translations: []
450
+ };
451
+ if (Array.isArray(translations)) {
452
+ local.translations = translations.map(entry => readTranslationFn(entry, pluginPath));
453
+ }
454
+ return local;
455
+ }
456
+
457
+ export function readTranslation(packageTranslation: RawTranslation, _pluginPath: string): NormalizedTranslation {
458
+ return {
459
+ id: packageTranslation.id,
460
+ path: packageTranslation.path
461
+ };
462
+ }
463
+
464
+ export function readWalkthroughs(
465
+ pck: PluginManifest,
466
+ ctx: Pick<NormalizeContributionsContext, 'resolveUrl' | 'onWarn'>
467
+ ): WalkthroughContribution[] | undefined {
468
+ const walkthroughs = rawContributes(pck).walkthroughs;
469
+ if (!walkthroughs) {
470
+ return undefined;
471
+ }
472
+ const pluginId = `${(pck.publisher ?? UNPUBLISHED).toLowerCase()}.${pck.name.toLowerCase()}`;
473
+ // `PluginModel` does not carry the extension icon, so it travels with the walkthroughs that show it.
474
+ const pluginIcon = pck.icon ? ctx.resolveUrl(pck.icon) : undefined;
475
+ const result: WalkthroughContribution[] = [];
476
+ for (const walkthrough of walkthroughs) {
477
+ if (typeof walkthrough.id !== 'string' || !walkthrough.id) {
478
+ ctx.onWarn(`'contributes.walkthroughs.id' of '${pluginId}' must be defined and non-empty`);
479
+ continue;
480
+ }
481
+ if (typeof walkthrough.title !== 'string' || !walkthrough.title) {
482
+ ctx.onWarn(`'contributes.walkthroughs.title' of '${pluginId}.${walkthrough.id}' must be defined and non-empty`);
483
+ continue;
484
+ }
485
+ if (!Array.isArray(walkthrough.steps)) {
486
+ ctx.onWarn(`'contributes.walkthroughs.steps' of '${pluginId}.${walkthrough.id}' must be an array`);
487
+ continue;
488
+ }
489
+ const steps: WalkthroughStepContribution[] = [];
490
+ for (const step of walkthrough.steps) {
491
+ if (typeof step.id !== 'string' || !step.id) {
492
+ ctx.onWarn(`'contributes.walkthroughs.steps.id' of '${pluginId}.${walkthrough.id}' must be defined and non-empty`);
493
+ continue;
494
+ }
495
+ if (typeof step.title !== 'string' || !step.title) {
496
+ ctx.onWarn(`'contributes.walkthroughs.steps.title' of '${pluginId}.${walkthrough.id}.${step.id}' must be defined and non-empty`);
497
+ continue;
498
+ }
499
+ steps.push({
500
+ id: step.id,
501
+ title: step.title,
502
+ description: step.description || '',
503
+ media: resolveWalkthroughMedia(ctx, step.media),
504
+ completionEvents: step.completionEvents,
505
+ when: step.when
506
+ });
507
+ }
508
+ result.push({
509
+ id: walkthrough.id,
510
+ title: walkthrough.title,
511
+ description: walkthrough.description || '',
512
+ steps,
513
+ when: walkthrough.when,
514
+ icon: walkthrough.icon,
515
+ pluginId,
516
+ pluginIcon
517
+ });
518
+ }
519
+ return result.length > 0 ? result : undefined;
520
+ }
521
+
522
+ function resolveWalkthroughMedia(
523
+ ctx: Pick<NormalizeContributionsContext, 'resolveUrl'>,
524
+ media?: PluginPackageWalkthroughStepMedia
525
+ ): WalkthroughStepMedia | undefined {
526
+ if (!media) {
527
+ return undefined;
528
+ }
529
+ if ('markdown' in media) {
530
+ return { markdown: ctx.resolveUrl(media.markdown) };
531
+ }
532
+ const altText = 'altText' in media ? media.altText as string : undefined;
533
+ if ('svg' in media) {
534
+ return { svg: ctx.resolveUrl(media.svg), ...(altText !== undefined && { altText }) };
535
+ }
536
+ if ('image' in media) {
537
+ if (typeof media.image === 'string') {
538
+ return { image: ctx.resolveUrl(media.image), ...(altText !== undefined && { altText }) };
539
+ }
540
+ // Only `dark` and `light` are mandatory; the high contrast variants fall back to them.
541
+ const dark = ctx.resolveUrl(media.image.dark);
542
+ const light = ctx.resolveUrl(media.image.light);
543
+ return {
544
+ image: {
545
+ dark,
546
+ light,
547
+ hc: media.image.hc ? ctx.resolveUrl(media.image.hc) : dark,
548
+ hcLight: media.image.hcLight ? ctx.resolveUrl(media.image.hcLight) : light
549
+ },
550
+ ...(altText !== undefined && { altText })
551
+ };
552
+ }
553
+ return undefined;
554
+ }
555
+
556
+ export function readCommand(ctx: NormalizeContributionsContext, command: RawCommand, pck: PluginManifest): NormalizedCommand {
557
+ const { themeIcon, iconUrl } = (ctx.transformIconUrl
558
+ ? ctx.transformIconUrl(pck, command.icon)
559
+ : transformIconUrl(ctx, pck, command.icon)) ?? {};
560
+ return {
561
+ command: command.command,
562
+ title: command.title,
563
+ shortTitle: command.shortTitle,
564
+ originalTitle: command.original,
565
+ category: command.category,
566
+ iconUrl,
567
+ themeIcon,
568
+ enablement: command.enablement
569
+ };
570
+ }
571
+
572
+ export function transformIconUrl(
573
+ ctx: Pick<NormalizeContributionsContext, 'resolveUrl'>,
574
+ _plugin: PluginManifest,
575
+ original?: IconUrl
576
+ ): { iconUrl?: IconUrl; themeIcon?: string } | undefined {
577
+ if (original) {
578
+ if (typeof original === 'string') {
579
+ if (original.startsWith('$(')) {
580
+ return { themeIcon: original };
581
+ }
582
+ return { iconUrl: ctx.resolveUrl(original) };
583
+ }
584
+ return {
585
+ iconUrl: {
586
+ light: ctx.resolveUrl(original.light),
587
+ dark: ctx.resolveUrl(original.dark)
588
+ }
589
+ };
590
+ }
591
+ return undefined;
592
+ }
593
+
594
+ export function readColors(ctx: NormalizeContributionsContext, pck: PluginManifest): ColorDefinition[] | undefined {
595
+ const colors = rawContributes(pck).colors;
596
+ if (!Array.isArray(colors)) {
597
+ return undefined;
598
+ }
599
+ const result: ColorDefinition[] = [];
600
+ for (const contribution of colors) {
601
+ if (!isObject(contribution)) {
602
+ continue;
603
+ }
604
+ if (typeof contribution.id !== 'string' || contribution.id.length === 0) {
605
+ ctx.onWarn("'configuration.colors.id' must be defined and can not be empty");
606
+ continue;
607
+ }
608
+ if (!contribution.id.match(colorIdPattern)) {
609
+ ctx.onWarn("'configuration.colors.id' must follow the word[.word]*");
610
+ continue;
611
+ }
612
+ if (typeof contribution.description !== 'string' || contribution.description.length === 0) {
613
+ ctx.onWarn("'configuration.colors.description' must be defined and can not be empty");
614
+ continue;
615
+ }
616
+ const defaults = contribution.defaults;
617
+ if (!isColorDefaults(defaults)) {
618
+ ctx.onWarn("'configuration.colors.defaults' must be defined and must contain 'light', 'dark' and 'highContrast'");
619
+ continue;
620
+ }
621
+ result.push({
622
+ id: contribution.id,
623
+ description: contribution.description,
624
+ defaults: {
625
+ light: defaults.light,
626
+ dark: defaults.dark,
627
+ hc: defaults.highContrast
628
+ }
629
+ });
630
+ }
631
+ return result;
632
+ }
633
+
634
+ export function readThemes(ctx: NormalizeContributionsContext, pck: PluginManifest): NormalizedTheme[] | undefined {
635
+ const themes = rawContributes(pck).themes;
636
+ if (!Array.isArray(themes)) {
637
+ return undefined;
638
+ }
639
+ const result: NormalizedTheme[] = [];
640
+ for (const contribution of themes) {
641
+ if (!isObject(contribution)) {
642
+ continue;
643
+ }
644
+ if (typeof contribution.path === 'string' && contribution.path) {
645
+ result.push({
646
+ id: typeof contribution.id === 'string' ? contribution.id : undefined,
647
+ uri: ctx.resolveUri(pck, contribution.path),
648
+ description: typeof contribution.description === 'string' ? contribution.description : undefined,
649
+ label: typeof contribution.label === 'string' ? contribution.label : undefined,
650
+ uiTheme: isPluginUiTheme(contribution.uiTheme) ? contribution.uiTheme : undefined,
651
+ });
652
+ }
653
+ }
654
+ return result;
655
+ }
656
+
657
+ export function readIconThemes(ctx: NormalizeContributionsContext, pck: PluginManifest): NormalizedIconTheme[] | undefined {
658
+ const iconThemes = rawContributes(pck).iconThemes;
659
+ if (!Array.isArray(iconThemes)) {
660
+ return undefined;
661
+ }
662
+ const result: NormalizedIconTheme[] = [];
663
+ for (const contribution of iconThemes) {
664
+ if (!isObject(contribution)) {
665
+ continue;
666
+ }
667
+ if (typeof contribution.id !== 'string' || !contribution.id) {
668
+ ctx.onWarn(`Expected string in \`contributes.iconThemes.id\`. Provided value: ${String(contribution.id)}`);
669
+ continue;
670
+ }
671
+ if (typeof contribution.path !== 'string' || !contribution.path) {
672
+ ctx.onWarn(`Expected string in \`contributes.iconThemes.path\`. Provided value: ${String(contribution.path)}`);
673
+ continue;
674
+ }
675
+ result.push({
676
+ id: contribution.id,
677
+ uri: ctx.resolveUri(pck, contribution.path),
678
+ description: typeof contribution.description === 'string' ? contribution.description : undefined,
679
+ label: typeof contribution.label === 'string' ? contribution.label : undefined,
680
+ uiTheme: isPluginUiTheme(contribution.uiTheme) ? contribution.uiTheme : undefined,
681
+ });
682
+ }
683
+ return result;
684
+ }
685
+
686
+ export function readIcons(ctx: NormalizeContributionsContext, pck: PluginManifest): NormalizedIcon[] | undefined {
687
+ const icons = rawContributes(pck).icons;
688
+ if (!isObject(icons)) {
689
+ return undefined;
690
+ }
691
+ const result: NormalizedIcon[] = [];
692
+ for (const id in icons) {
693
+ if (!Object.prototype.hasOwnProperty.call(icons, id)) {
694
+ continue;
695
+ }
696
+ if (!id.match(iconIdPattern)) {
697
+ ctx.onWarn(
698
+ '\'configuration.icons\' keys represent the icon id and can only contain letter, digits and minuses. ' +
699
+ 'They need to consist of at least two segments in the form `component-iconname`. ' +
700
+ `extension: ${pck.name} icon id: ${id}`
701
+ );
702
+ return;
703
+ }
704
+ const iconContribution = icons[id];
705
+ if (!isObject(iconContribution) || typeof iconContribution.description !== 'string' || iconContribution.description.length === 0) {
706
+ ctx.onWarn(`configuration.icons.description must be defined and can not be empty, extension: ${pck.name} icon id: ${id}`);
707
+ return;
708
+ }
709
+
710
+ const defaultIcon = iconContribution.default;
711
+ if (typeof defaultIcon === 'string') {
712
+ result.push({
713
+ id,
714
+ extensionId: `${pck.publisher}.${pck.name}`,
715
+ description: iconContribution.description,
716
+ defaults: { id: defaultIcon }
717
+ });
718
+ } else if (isObject(defaultIcon) && typeof defaultIcon.fontPath === 'string' && typeof defaultIcon.fontCharacter === 'string') {
719
+ const format = getFileExtension(defaultIcon.fontPath);
720
+ if (['woff', 'woff2', 'ttf'].indexOf(format) === -1) {
721
+ ctx.onWarn(`Expected \`contributes.icons.default.fontPath\` to have file extension 'woff', woff2' or 'ttf', is '${format}'.`);
722
+ return;
723
+ }
724
+
725
+ const iconFontLocation = ctx.resolveUri(pck, defaultIcon.fontPath);
726
+ result.push({
727
+ id,
728
+ extensionId: `${pck.publisher}.${pck.name}`,
729
+ description: iconContribution.description,
730
+ defaults: {
731
+ fontCharacter: defaultIcon.fontCharacter,
732
+ location: iconFontLocation
733
+ }
734
+ });
735
+ } else {
736
+ ctx.onWarn(
737
+ "'configuration.icons.default' must be either a reference to the id of an other theme icon (string) " +
738
+ 'or a icon definition (object) with properties `fontPath` and `fontCharacter`.'
739
+ );
740
+ }
741
+ }
742
+ return result;
743
+ }
744
+
745
+ export function readSnippets(ctx: NormalizeContributionsContext, pck: PluginManifest): NormalizedSnippet[] | undefined {
746
+ const snippets = rawContributes(pck).snippets;
747
+ if (!Array.isArray(snippets)) {
748
+ return undefined;
749
+ }
750
+ const result: NormalizedSnippet[] = [];
751
+ for (const contribution of snippets) {
752
+ if (!isObject(contribution)) {
753
+ continue;
754
+ }
755
+ if (typeof contribution.path === 'string' && contribution.path) {
756
+ result.push({
757
+ language: typeof contribution.language === 'string' ? contribution.language : undefined,
758
+ source: pck.displayName || pck.name,
759
+ uri: ctx.resolveUri(pck, contribution.path)
760
+ });
761
+ }
762
+ }
763
+ return result;
764
+ }
765
+
766
+ export function readConfiguration(rawConfiguration: IConfigurationNode, _pluginPath: string): PreferenceSchema | undefined {
767
+ const { scope, overridable } = getScope(rawConfiguration.scope);
768
+ const schema: PreferenceSchema = {
769
+ scope,
770
+ defaultOverridable: overridable,
771
+ title: rawConfiguration.title,
772
+ properties: {}
773
+ };
774
+
775
+ if (rawConfiguration.properties) {
776
+ for (const [key, property] of Object.entries(rawConfiguration.properties)) {
777
+ const scopeInfo = getScope(property.scope);
778
+ const schemaProperty: PreferenceDataProperty = {
779
+ ...property,
780
+ scope: scopeInfo.scope,
781
+ overridable: scopeInfo.overridable,
782
+ default: property.default !== undefined ? property.default : deriveDefaultForType(property.type)
783
+ };
784
+
785
+ schema.properties[key] = schemaProperty;
786
+ }
787
+ }
788
+ return schema;
789
+ }
790
+
791
+ export function readKeybinding(rawKeybinding: RawKeybinding): NormalizedKeybinding {
792
+ return {
793
+ keybinding: rawKeybinding.key,
794
+ command: rawKeybinding.command,
795
+ when: rawKeybinding.when,
796
+ mac: rawKeybinding.mac,
797
+ linux: rawKeybinding.linux,
798
+ win: rawKeybinding.win,
799
+ args: rawKeybinding.args
800
+ };
801
+ }
802
+
803
+ export function readCustomEditors(rawCustomEditors: readonly RawCustomEditor[]): NormalizedCustomEditor[] {
804
+ return rawCustomEditors.map(rawCustomEditor => readCustomEditor(rawCustomEditor));
805
+ }
806
+
807
+ export function readCustomEditor(rawCustomEditor: RawCustomEditor): NormalizedCustomEditor {
808
+ return {
809
+ viewType: rawCustomEditor.viewType,
810
+ displayName: rawCustomEditor.displayName,
811
+ selector: rawCustomEditor.selector || [],
812
+ priority: toCustomEditorPriority(rawCustomEditor.priority)
813
+ };
814
+ }
815
+
816
+ export function readViewsContainers(
817
+ ctx: NormalizeContributionsContext,
818
+ rawViewsContainers: readonly RawViewContainer[],
819
+ pck: PluginManifest
820
+ ): NormalizedViewContainer[] {
821
+ return rawViewsContainers
822
+ .map(rawViewContainer => readViewContainer(ctx, rawViewContainer, pck))
823
+ .filter((container): container is NormalizedViewContainer => container !== undefined);
824
+ }
825
+
826
+ export function readViewContainer(
827
+ ctx: NormalizeContributionsContext,
828
+ rawViewContainer: RawViewContainer,
829
+ pck: PluginManifest
830
+ ): NormalizedViewContainer | undefined {
831
+ const icon = rawViewContainer.icon;
832
+ if (typeof icon !== 'string') {
833
+ ctx.onError('viewsContainers', 'view container icon must be defined and must be a string', rawViewContainer);
834
+ return undefined;
835
+ }
836
+ const themeIcon = icon.startsWith('$(') ? icon : undefined;
837
+ const iconUrl = ctx.resolveUrl(icon);
838
+ return {
839
+ id: rawViewContainer.id,
840
+ title: rawViewContainer.title,
841
+ iconUrl,
842
+ themeIcon,
843
+ when: rawViewContainer.when,
844
+ };
845
+ }
846
+
847
+ export function readViews(rawViews: readonly RawView[]): RawView[] {
848
+ return rawViews.map(rawView => readView(rawView));
849
+ }
850
+
851
+ export function readView(rawView: RawView): RawView {
852
+ return {
853
+ id: rawView.id,
854
+ name: rawView.name,
855
+ when: rawView.when,
856
+ type: rawView.type
857
+ };
858
+ }
859
+
860
+ export function readViewsWelcome(rawViewsWelcome: readonly RawViewWelcome[], rowViews: ViewsByLocation | undefined): NormalizedViewWelcome[] {
861
+ return rawViewsWelcome.map(rawViewWelcome => readViewWelcome(rawViewWelcome, extractPluginViewsIds(rowViews)));
862
+ }
863
+
864
+ export function readViewWelcome(rawViewWelcome: RawViewWelcome, pluginViewsIds: string[]): NormalizedViewWelcome {
865
+ return {
866
+ view: rawViewWelcome.view,
867
+ content: rawViewWelcome.contents,
868
+ when: rawViewWelcome.when,
869
+ enablement: rawViewWelcome.enablement,
870
+ order: pluginViewsIds.findIndex(v => v === rawViewWelcome.view) > -1 ? 0 : 1
871
+ };
872
+ }
873
+
874
+ export function extractPluginViewsIds(views: ViewsByLocation | undefined): string[] {
875
+ const pluginViewsIds: string[] = [];
876
+ if (views) {
877
+ for (const location of Object.keys(views)) {
878
+ const viewsIds = views[location]
879
+ .filter(isObject)
880
+ .map(view => view.id)
881
+ .filter((id): id is string => typeof id === 'string');
882
+ pluginViewsIds.push(...viewsIds);
883
+ }
884
+ }
885
+ return pluginViewsIds;
886
+ }
887
+
888
+ export function readMenus(rawMenus: readonly RawMenu[]): RawMenu[] {
889
+ return rawMenus.map(rawMenu => readMenu(rawMenu));
890
+ }
891
+
892
+ export function readMenu(rawMenu: RawMenu): RawMenu {
893
+ return {
894
+ command: rawMenu.command,
895
+ submenu: rawMenu.submenu,
896
+ alt: rawMenu.alt,
897
+ group: rawMenu.group,
898
+ when: rawMenu.when
899
+ };
900
+ }
901
+
902
+ export async function readLanguages(ctx: NormalizeContributionsContext, rawLanguages: readonly RawLanguage[], plugin: PluginManifest): Promise<NormalizedLanguage[]> {
903
+ return Promise.all(rawLanguages.map(language => readLanguage(ctx, language, plugin)));
904
+ }
905
+
906
+ export function readSubmenus(ctx: NormalizeContributionsContext, rawSubmenus: readonly RawSubmenu[], plugin: PluginManifest): NormalizedSubmenu[] {
907
+ return rawSubmenus.map(submenu => readSubmenu(ctx, submenu, plugin));
908
+ }
909
+
910
+ export function readSubmenu(ctx: NormalizeContributionsContext, rawSubmenu: RawSubmenu, plugin: PluginManifest): NormalizedSubmenu {
911
+ const icon = ctx.transformIconUrl
912
+ ? ctx.transformIconUrl(plugin, rawSubmenu.icon)
913
+ : transformIconUrl(ctx, plugin, rawSubmenu.icon);
914
+ return {
915
+ icon: icon?.iconUrl ?? icon?.themeIcon,
916
+ id: rawSubmenu.id,
917
+ label: rawSubmenu.label
918
+ };
919
+ }
920
+
921
+ export async function readLanguage(ctx: NormalizeContributionsContext, rawLang: RawLanguage, plugin: PluginManifest): Promise<NormalizedLanguage> {
922
+ const icon = ctx.transformIconUrl
923
+ ? ctx.transformIconUrl(plugin, rawLang.icon)
924
+ : transformIconUrl(ctx, plugin, rawLang.icon);
925
+ const result: NormalizedLanguage = {
926
+ id: rawLang.id,
927
+ aliases: rawLang.aliases,
928
+ extensions: rawLang.extensions,
929
+ filenamePatterns: rawLang.filenamePatterns,
930
+ filenames: rawLang.filenames,
931
+ firstLine: rawLang.firstLine,
932
+ mimetypes: rawLang.mimetypes,
933
+ icon: icon?.iconUrl ?? icon?.themeIcon
934
+ };
935
+ if (typeof rawLang.configuration === 'string') {
936
+ const rawConfiguration = await readJson(ctx, path.resolve(plugin.packagePath, rawLang.configuration));
937
+ if (isObject(rawConfiguration)) {
938
+ const configuration: NormalizedLanguageConfiguration = {
939
+ brackets: Array.isArray(rawConfiguration.brackets)
940
+ ? rawConfiguration.brackets.filter(isCharacterPair)
941
+ : undefined,
942
+ comments: rawConfiguration.comments as CommentRule | undefined,
943
+ folding: rawConfiguration.folding as FoldingRules | undefined,
944
+ wordPattern: typeof rawConfiguration.wordPattern === 'string' ? rawConfiguration.wordPattern : undefined,
945
+ autoClosingPairs: extractValidAutoClosingPairs(ctx, rawLang.id, rawConfiguration),
946
+ indentationRules: rawConfiguration.indentationRules as IndentationRules | undefined,
947
+ surroundingPairs: extractValidSurroundingPairs(ctx, rawLang.id, rawConfiguration),
948
+ onEnterRules: Array.isArray(rawConfiguration.onEnterRules) ? rawConfiguration.onEnterRules as NormalizedLanguageConfiguration['onEnterRules'] : undefined,
949
+ };
950
+ result.configuration = configuration;
951
+ }
952
+ }
953
+ return result;
954
+ }
955
+
956
+ export function readDebuggers(rawDebuggers: readonly RawDebugger[]): RawDebugger[] {
957
+ return rawDebuggers.map(rawDebug => readDebugger(rawDebug));
958
+ }
959
+
960
+ export function readDebugger(rawDebugger: RawDebugger): RawDebugger {
961
+ return {
962
+ type: rawDebugger.type,
963
+ label: rawDebugger.label,
964
+ languages: rawDebugger.languages,
965
+ enableBreakpointsFor: rawDebugger.enableBreakpointsFor,
966
+ variables: rawDebugger.variables,
967
+ adapterExecutableCommand: rawDebugger.adapterExecutableCommand,
968
+ configurationSnippets: rawDebugger.configurationSnippets,
969
+ win: rawDebugger.win,
970
+ winx86: rawDebugger.winx86,
971
+ windows: rawDebugger.windows,
972
+ osx: rawDebugger.osx,
973
+ linux: rawDebugger.linux,
974
+ program: rawDebugger.program,
975
+ args: rawDebugger.args,
976
+ runtime: rawDebugger.runtime,
977
+ runtimeArgs: rawDebugger.runtimeArgs,
978
+ configurationAttributes: rawDebugger.configurationAttributes
979
+ };
980
+ }
981
+
982
+ export function readTaskDefinition(
983
+ pluginName: string,
984
+ definitionContribution: RawTaskDefinition,
985
+ ctx?: Pick<NormalizeContributionsContext, 'toSchema'>
986
+ ): TaskDefinition {
987
+ const propertyKeys = definitionContribution.properties && isObject(definitionContribution.properties)
988
+ ? Object.keys(definitionContribution.properties)
989
+ : [];
990
+ const schema = (ctx?.toSchema ?? toSchema)(definitionContribution);
991
+ return {
992
+ taskType: definitionContribution.type,
993
+ source: pluginName,
994
+ properties: {
995
+ required: definitionContribution.required,
996
+ all: propertyKeys,
997
+ schema
998
+ }
999
+ };
1000
+ }
1001
+
1002
+ export function toSchema(definition: RawTaskDefinition): IJSONSchema {
1003
+ const reconciliation: IJSONSchema = { ...(definition as IJSONSchema), type: 'object' };
1004
+ const schema = deepClone(reconciliation);
1005
+ if (schema.properties === undefined) {
1006
+ // eslint-disable-next-line no-null/no-null
1007
+ schema.properties = Object.create(null);
1008
+ }
1009
+ schema.type = 'object';
1010
+ schema.properties!.type = { type: 'string', const: definition.type as string };
1011
+ return schema;
1012
+ }
1013
+
1014
+ function extractValidAutoClosingPairs(ctx: NormalizeContributionsContext, langId: string, configuration: Record<string, unknown>): AutoClosingPairConditional[] | undefined {
1015
+ const source = configuration.autoClosingPairs;
1016
+ if (typeof source === 'undefined') {
1017
+ return undefined;
1018
+ }
1019
+ if (!Array.isArray(source)) {
1020
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs\` to be an array.`);
1021
+ return undefined;
1022
+ }
1023
+
1024
+ let result: AutoClosingPairConditional[] | undefined = undefined;
1025
+ for (let i = 0, len = source.length; i < len; i++) {
1026
+ const pair = source[i];
1027
+ if (Array.isArray(pair)) {
1028
+ if (!isCharacterPair(pair)) {
1029
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
1030
+ continue;
1031
+ }
1032
+ result = result || [];
1033
+ result.push({ open: pair[0], close: pair[1] });
1034
+ } else {
1035
+ if (!isObject(pair)) {
1036
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
1037
+ continue;
1038
+ }
1039
+ if (typeof pair.open !== 'string') {
1040
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs[${i}].open\` to be a string.`);
1041
+ continue;
1042
+ }
1043
+ if (typeof pair.close !== 'string') {
1044
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs[${i}].close\` to be a string.`);
1045
+ continue;
1046
+ }
1047
+ if (typeof pair.notIn !== 'undefined') {
1048
+ if (!isStringArray(pair.notIn)) {
1049
+ ctx.onWarn(`[${langId}]: language configuration: expected \`autoClosingPairs[${i}].notIn\` to be a string array.`);
1050
+ continue;
1051
+ }
1052
+ }
1053
+ result = result || [];
1054
+ result.push({ open: pair.open, close: pair.close, notIn: pair.notIn });
1055
+ }
1056
+ }
1057
+ return result;
1058
+ }
1059
+
1060
+ function extractValidSurroundingPairs(ctx: NormalizeContributionsContext, langId: string, configuration: Record<string, unknown>): AutoClosingPair[] | undefined {
1061
+ const source = configuration.surroundingPairs;
1062
+ if (typeof source === 'undefined') {
1063
+ return undefined;
1064
+ }
1065
+ if (!Array.isArray(source)) {
1066
+ ctx.onWarn(`[${langId}]: language configuration: expected \`surroundingPairs\` to be an array.`);
1067
+ return undefined;
1068
+ }
1069
+
1070
+ let result: AutoClosingPair[] | undefined = undefined;
1071
+ for (let i = 0, len = source.length; i < len; i++) {
1072
+ const pair = source[i];
1073
+ if (Array.isArray(pair)) {
1074
+ if (!isCharacterPair(pair)) {
1075
+ ctx.onWarn(`[${langId}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
1076
+ continue;
1077
+ }
1078
+ result = result || [];
1079
+ result.push({ open: pair[0], close: pair[1] });
1080
+ } else {
1081
+ if (!isObject(pair)) {
1082
+ ctx.onWarn(`[${langId}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
1083
+ continue;
1084
+ }
1085
+ if (typeof pair.open !== 'string') {
1086
+ ctx.onWarn(`[${langId}]: language configuration: expected \`surroundingPairs[${i}].open\` to be a string.`);
1087
+ continue;
1088
+ }
1089
+ if (typeof pair.close !== 'string') {
1090
+ ctx.onWarn(`[${langId}]: language configuration: expected \`surroundingPairs[${i}].close\` to be a string.`);
1091
+ continue;
1092
+ }
1093
+ result = result || [];
1094
+ result.push({ open: pair.open, close: pair.close });
1095
+ }
1096
+ }
1097
+ return result;
1098
+ }