src/vs/workbench/services/preferences/common/preferencesValidation.ts

426 LOC · 387 covered · 39 uncovered · 83 ranges · 42 concepts · 26 introducers · 29 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filepreferencesModels.ts ×2 · 4 introduced LOCpreferencesModels.ts ×2preferencesModels.test|title=DefaultSettings separates groups with same id but different titles|occurrence=1, preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1 · 0 introduced LOCpreferencesModels.test|t…preferencesModels.ts ×2 · 2 introduced LOCpreferencesModels.ts ×2preferencesModels.test|title=DefaultSettings groups settings by title when they share the same extension id|occurrence=1 · 0 introduced LOCpreferencesModels.test|t…preferencesModels.ts ×2 · 6 introduced LOCpreferencesModels.ts ×2preferencesModels.ts ×1 · 1 introduced LOCpreferencesModels.ts ×1preferencesModels.ts ×2 · 7 introduced LOCpreferencesModels.ts ×2preferencesModels.ts ×1 · 2 introduced LOCpreferencesModels.ts ×1preferencesModels.ts ×3 · 3 introduced LOCpreferencesModels.ts ×3preferencesModels.ts ×2 · 3 introduced LOCpreferencesModels.ts ×2preferencesModels.ts ×1 · 3 introduced LOCpreferencesModels.ts ×1preferencesModels.ts ×90 · 441 introduced LOCpreferencesModels.ts ×90preferencesValidation.ts ×1 · 7 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 6 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×4 · 25 introduced LOCpreferencesValidation.ts…preferencesValidation.test|title=Preferences Validation numerical objects work|occurrence=1 · 0 introduced LOCpreferencesValidation.te…preferencesValidation.ts ×2 · 8 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 4 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 3 introduced LOCpreferencesValidation.ts…preferencesValidation.test|title=Preferences Validation array of enums|occurrence=1 · 0 introduced LOCpreferencesValidation.te…preferencesValidation.test|title=Preferences Validation min-max and enum|occurrence=1 · 0 introduced LOCpreferencesValidation.te…preferencesValidation.ts ×1 · 5 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 12 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×3 · 6 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×6 · 37 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 4 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 3 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 2 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×9 · 29 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×5 · 26 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 3 introduced LOCpreferencesValidation.ts…preferencesValidation.test|title=Preferences Validation string max min length work|occurrence=1 · 0 introduced LOCpreferencesValidation.te…preferencesValidation.ts ×3 · 6 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 5 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×2 · 8 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 2 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×7 · 17 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 3 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×1 · 2 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×12 · 107 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×4 · 23 introduced LOCpreferencesValidation.ts…preferencesValidation.ts ×12 · 34 introduced LOCpreferencesValidation.ts…preferencesModels.test|title=DefaultSettings groups settings by id when they share the same extension id and have no title|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings groups settings by id when they share the same extension id and have no title|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings groups settings by title when they share the same extension id|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings groups settings by title when they share the same extension id|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings groups settings correctly when extension id is same as group id|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings merges untitled group into titled group if id matches|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings merges untitled group into titled group if id matches|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings separates groups with same id (no title) but different extension ids|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings separates groups with same id (no title) but different extension ids|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings separates groups with same id and title but different extension ids|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings separates groups with same id and title but different extension ids|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings separates groups with same id but different titles|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings separates groups with same id but different titles|occurrence=1preferencesModels.test|t…preferencesModels.test|title=DefaultSettings sorts groups by order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesModels.test|title=DefaultSettings sorts groups by order|occurrence=1preferencesModels.test|t…preferencesValidation.test|title=Preferences Validation Unicode pattern|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation Unicode pattern|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation array of enums|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation array of enums|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation array of numbers|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation array of numbers|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation custom error messages are shown|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation custom error messages are shown|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation exclusive max and max work together properly|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation exclusive max and max work together properly|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation exclusive min and min work together properly|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation exclusive min and min work together properly|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation getInvalidTypeError|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation getInvalidTypeError|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation integer type correctly adds a validation|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation integer type correctly adds a validation|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation min-max and enum|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation min-max and enum|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation min-max items array|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation min-max items array|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation multiple of works for both integers and fractions|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation multiple of works for both integers and fractions|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation null is allowed only when expected|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation null is allowed only when expected|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation numerical objects work|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation numerical objects work|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation objects work|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation objects work|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation pattern with error message|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation pattern with error message|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation patterns work|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation patterns work|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation pattern|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation pattern|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation simple array|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation simple array|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation string max min length work|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation string max min length work|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation uniqueItems|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation uniqueItems|occurrence=1preferencesValidation.te…preferencesValidation.test|title=Preferences Validation uri checks work|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/preferences/test/common/preferencesValidation.test|title=Preferences Validation uri checks work|occurrence=1preferencesValidation.te…Focused file · src/vs/workbench/services/preferences/common/preferencesValidation.ts · 426 LOCcommon/preferencesValida…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- preferencesValidation.ts ×12
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 * as nls from '../../../../nls.js';
7 > import { JSONSchemaType } from '../../../../base/common/jsonSchema.js';
8 > import { Color } from '../../../../base/common/color.js';
9 > import { isObject, isUndefinedOrNull, isString, isStringArray } from '../../../../base/common/types.js';
10 > import { IConfigurationPropertySchema } from '../../../../platform/configuration/common/configurationRegistry.js';
11 >
12 > type Validator<T> = { enabled: boolean; isValid: (value: T) => boolean; message: string };
13 >
14 > function canBeType(propTypes: (string | undefined)[], ...types: JSONSchemaType[]): boolean { preferencesValidation.ts ×12
15 > return types.some(t => propTypes.includes(t));
16 > }
18 > function isNullOrEmpty(value: unknown): boolean { preferencesValidation.ts ×5
19 > return value === '' || isUndefinedOrNull(value);
20 > }
22 > export function createValidator(prop: IConfigurationPropertySchema): (value: any) => (string | null) {
23 > const type: (string | undefined)[] = Array.isArray(prop.type) ? prop.type : [prop.type]; preferencesValidation.ts ×12
24 > const isNullable = canBeType(type, 'null');
25 > const isNumeric = (canBeType(type, 'number') || canBeType(type, 'integer')) && (type.length === 1 || type.length === 2 && isNullable);
26 >
27 > const numericValidations = getNumericValidators(prop);
28 > const stringValidations = getStringValidators(prop);
29 > const arrayValidator = getArrayValidator(prop);
30 > const objectValidator = getObjectValidator(prop);
31 >
32 > return value => {
33 > if (isNullable && isNullOrEmpty(value)) { return ''; } preferencesValidation.ts ×7
34 >
35 > const errors: string[] = [];
36 > if (arrayValidator) {
37 > const err = arrayValidator(value); preferencesValidation.ts ×9
38 > if (err) {
39 > errors.push(err);
40 > }
41 > }
43 > if (objectValidator) {
44 > const err = objectValidator(value); preferencesValidation.ts ×6
45 > if (err) {
46 > errors.push(err);
47 > }
48 > }
50 > if (prop.type === 'boolean' && value !== true && value !== false) {
51 > errors.push(nls.localize('validations.booleanIncorrectType', 'Incorrect type. Expected "boolean".')); preferencesValidation.ts ×4
52 > }
54 > if (isNumeric) {
55 > if (isNullOrEmpty(value) || typeof value === 'boolean' || Array.isArray(value) || isNaN(+value)) { preferencesValidation.ts ×5
56 > errors.push(nls.localize('validations.expectedNumeric', "Value must be a number."));
57 > } else {
58 > errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message));
59 > }
60 > }
62 > if (prop.type === 'string') {
63 > if (prop.enum && !isStringArray(prop.enum)) { preferencesValidation.ts ×3
64 errors.push(nls.localize('validations.stringIncorrectEnumOptions', 'The enum options should be strings, but there is a non-string option. Please file an issue with the extension author.'));
65 > } else if (!isString(value)) { preferencesValidation.ts ×3
66 > errors.push(nls.localize('validations.stringIncorrectType', 'Incorrect type. Expected "string".')); preferencesValidation.ts ×4
68 > errors.push(...stringValidations.filter(validator => !validator.isValid(value)).map(validator => validator.message));
69 > }
70 > }
72 > if (errors.length) {
73 > return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
74 > }
76 > return '';
80 > /**
81 > * Returns an error string if the value is invalid and can't be displayed in the settings UI for the given type.
82 > */
83 > export function getInvalidTypeError(value: any, type: undefined | string | string[]): string | undefined {
84 > if (typeof type === 'undefined') { preferencesValidation.ts ×4
85 return;
86 }
88 > const typeArr = Array.isArray(type) ? type : [type];
89 > if (!typeArr.some(_type => valueValidatesAsType(value, _type))) {
90 > return nls.localize('invalidTypeError', "Setting has an invalid type, expected {0}. Fix in JSON.", JSON.stringify(type));
91 > }
92 >
93 > return;
94 > }
96 > function valueValidatesAsType(value: any, type: string): boolean { preferencesValidation.ts ×4
97 > const valueType = typeof value;
98 > if (type === 'boolean') {
99 > return valueType === 'boolean';
100 > } else if (type === 'object') {
101 return value && !Array.isArray(value) && valueType === 'object';
102 > } else if (type === 'null') { preferencesValidation.ts ×4
103 > return value === null;
104 > } else if (type === 'array') {
105 > return Array.isArray(value);
106 > } else if (type === 'string') {
107 > return valueType === 'string';
108 > } else if (type === 'number' || type === 'integer') {
109 > return valueType === 'number';
110 > }
111
112 return true;
113 }
115 > function toRegExp(pattern: string): RegExp { preferencesValidation.ts ×2
116 > try {
117 > // The u flag allows support for better Unicode matching,
118 > // but deprecates some patterns such as [\s-9]
119 > // Ref https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Character_class#description
120 > return new RegExp(pattern, 'u');
121 > } catch (e) {
122 try {
123 return new RegExp(pattern);
124 } catch (e) {
125 // If the pattern can't be parsed even without the 'u' flag,
126 // just log the error to avoid rendering the entire Settings editor blank.
127 // Ref https://github.com/microsoft/vscode/issues/195054
128 console.error(nls.localize('regexParsingError', "Error parsing the following regex both with and without the u flag:"), pattern);
129 return /.*/;
130 }
131 }
134 > function getStringValidators(prop: IConfigurationPropertySchema) { preferencesValidation.ts ×12
135 > const uriRegex = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
136 > let patternRegex: RegExp | undefined;
137 > if (typeof prop.pattern === 'string') {
138 > patternRegex = toRegExp(prop.pattern); preferencesValidation.ts ×1
139 > }
141 > return [
142 > {
143 > enabled: prop.maxLength !== undefined,
144 > isValid: ((value: { length: number }) => value.length <= prop.maxLength!),
145 > message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength)
146 > },
147 > {
148 > enabled: prop.minLength !== undefined,
149 > isValid: ((value: { length: number }) => value.length >= prop.minLength!),
150 > message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength)
151 > },
152 > {
153 > enabled: patternRegex !== undefined,
154 > isValid: ((value: string) => patternRegex!.test(value)),
155 > message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern)
156 > },
157 > {
158 > enabled: prop.format === 'color-hex',
159 > isValid: ((value: string) => Color.Format.CSS.parseHex(value)),
160 > message: nls.localize('validations.colorFormat', "Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.")
161 > },
162 > {
163 > enabled: prop.format === 'uri' || prop.format === 'uri-reference',
164 > isValid: ((value: string) => !!value.length),
165 > message: nls.localize('validations.uriEmpty', "URI expected.")
166 > },
167 > {
168 > enabled: prop.format === 'uri' || prop.format === 'uri-reference',
169 > isValid: ((value: string) => uriRegex.test(value)),
170 > message: nls.localize('validations.uriMissing', "URI is expected.")
171 > },
172 > {
173 > enabled: prop.format === 'uri',
174 > isValid: ((value: string) => {
175 > const matches = value.match(uriRegex); preferencesValidation.ts ×1
176 > return !!(matches && matches[2]);
177 > }),
178 > message: nls.localize('validations.uriSchemeMissing', "URI with a scheme is expected.") preferencesValidation.ts ×12
179 > },
180 > {
181 > enabled: prop.enum !== undefined,
182 > isValid: ((value: string) => {
183 return prop.enum!.includes(value);
184 }),
185 > message: nls.localize('validations.invalidStringEnumValue', "Value is not accepted. Valid values: {0}.", preferencesValidation.ts ×12
186 > prop.enum ? prop.enum.map(key => `"${key}"`).join(', ') : '[]')
187 > }
188 > ].filter(validation => validation.enabled);
189 > }
191 > function getNumericValidators(prop: IConfigurationPropertySchema): Validator<number>[] { preferencesValidation.ts ×12
192 > const type: (string | undefined)[] = Array.isArray(prop.type) ? prop.type : [prop.type];
193 >
194 > const isNullable = canBeType(type, 'null');
195 > const isIntegral = (canBeType(type, 'integer')) && (type.length === 1 || type.length === 2 && isNullable);
196 > const isNumeric = canBeType(type, 'number', 'integer') && (type.length === 1 || type.length === 2 && isNullable);
197 > if (!isNumeric) {
198 > return []; preferencesValidation.ts ×1
199 > }
201 > let exclusiveMax: number | undefined;
202 > let exclusiveMin: number | undefined;
203 >
204 > if (typeof prop.exclusiveMaximum === 'boolean') {
205 exclusiveMax = prop.exclusiveMaximum ? prop.maximum : undefined;
207 > exclusiveMax = prop.exclusiveMaximum;
208 > }
209 >
210 > if (typeof prop.exclusiveMinimum === 'boolean') {
211 exclusiveMin = prop.exclusiveMinimum ? prop.minimum : undefined;
213 > exclusiveMin = prop.exclusiveMinimum;
214 > }
215 >
216 > return [
217 > {
218 > enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum),
219 > isValid: ((value: number) => value < exclusiveMax!), preferencesValidation.ts ×12
220 > message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax)
221 > },
222 > {
223 > enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum),
224 > isValid: ((value: number) => value > exclusiveMin!),
225 > message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin)
226 > },
227 > {
228 > enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum),
229 > isValid: ((value: number) => value <= prop.maximum!),
230 > message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum)
231 > },
232 > {
233 > enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum),
234 > isValid: ((value: number) => value >= prop.minimum!),
235 > message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum)
236 > },
237 > {
238 > enabled: prop.multipleOf !== undefined,
239 > isValid: ((value: number) => value % prop.multipleOf! === 0),
240 > message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf)
241 > },
242 > {
243 > enabled: isIntegral,
244 > isValid: ((value: number) => value % 1 === 0),
245 > message: nls.localize('validations.expectedInteger', "Value must be an integer.")
246 > },
247 > ].filter(validation => validation.enabled);
248 > }
250 > function getArrayValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { preferencesValidation.ts ×12
251 > if (prop.type === 'array' && prop.items && !Array.isArray(prop.items)) {
252 > const propItems = prop.items; preferencesValidation.ts ×9
253 > if (propItems && !Array.isArray(propItems.type)) {
254 > const withQuotes = (s: string) => `'` + s + `'`;
255 > return value => {
256 > if (!value) {
257 return null;
258 }
260 > let message = '';
261 >
262 > if (!Array.isArray(value)) {
263 > message += nls.localize('validations.arrayIncorrectType', 'Incorrect type. Expected an array.'); preferencesValidation.ts ×1
264 > message += '\n';
265 > return message;
266 > }
268 > const arrayValue = value as unknown[];
269 > if (prop.uniqueItems) {
270 > if (new Set(arrayValue).size < arrayValue.length) { preferencesValidation.ts ×1
271 > message += nls.localize('validations.stringArrayUniqueItems', 'Array has duplicate items');
272 > message += '\n';
273 > }
274 > }
276 > if (prop.minItems && arrayValue.length < prop.minItems) {
277 > message += nls.localize('validations.stringArrayMinItem', 'Array must have at least {0} items', prop.minItems); preferencesValidation.ts ×1
278 > message += '\n';
279 > }
281 > if (prop.maxItems && arrayValue.length > prop.maxItems) {
282 > message += nls.localize('validations.stringArrayMaxItem', 'Array must have at most {0} items', prop.maxItems); preferencesValidation.ts ×1
283 > message += '\n';
284 > }
286 > if (propItems.type === 'string') {
287 > if (!isStringArray(arrayValue)) { preferencesValidation.ts ×3
288 > message += nls.localize('validations.stringArrayIncorrectType', 'Incorrect type. Expected a string array.'); preferencesValidation.ts ×1
289 > message += '\n';
290 > return message;
291 > }
293 > if (typeof propItems.pattern === 'string') {
294 > const patternRegex = toRegExp(propItems.pattern); preferencesValidation.ts ×2
295 > arrayValue.forEach(v => {
296 > if (!patternRegex.test(v)) {
297 > message +=
298 > propItems.patternErrorMessage ||
299 > nls.localize( preferencesValidation.ts ×1
300 > 'validations.stringArrayItemPattern',
301 > 'Value {0} must match regex {1}.',
302 > withQuotes(v),
303 > withQuotes(propItems.pattern!)
304 > );
306 > });
307 > }
309 > const propItemsEnum = propItems.enum;
310 > if (propItemsEnum) {
311 > arrayValue.forEach(v => { preferencesValidation.ts ×1
312 > if (propItemsEnum.indexOf(v) === -1) {
313 > message += nls.localize(
314 > 'validations.stringArrayItemEnum',
315 > 'Value {0} is not one of {1}',
316 > withQuotes(v),
317 > '[' + propItemsEnum.map(withQuotes).join(', ') + ']'
318 > );
319 > message += '\n';
320 > }
321 > });
322 > }
323 > } else if (propItems.type === 'integer' || propItems.type === 'number') { preferencesValidation.ts ×9
324 > arrayValue.forEach(v => { preferencesValidation.ts ×1
325 > const errorMessage = getErrorsForSchema(propItems, v);
326 > if (errorMessage) {
327 > message += `${v}: ${errorMessage}\n`;
328 > }
329 > });
330 > }
332 > return message;
333 > };
334 > }
335 > }
337 > return null;
338 > }
340 > function getObjectValidator(prop: IConfigurationPropertySchema): ((value: any) => (string | null)) | null { preferencesValidation.ts ×12
341 > if (prop.type === 'object') {
342 > const { properties, patternProperties, additionalProperties, propertyNames } = prop; preferencesValidation.ts ×6
343 > return value => {
344 > if (!value) {
345 return null;
346 }
348 > const errors: string[] = [];
349 > let propertyNamesErrorShown = false;
350 >
351 > if (!isObject(value)) {
352 > errors.push(nls.localize('validations.objectIncorrectType', 'Incorrect type. Expected an object.')); preferencesValidation.ts ×4
354 > Object.keys(value).forEach((key: string) => {
355 > const data = value[key];
356 >
357 > // Validate propertyNames.pattern - show error message once
358 > if (propertyNames?.pattern && !propertyNamesErrorShown) {
359 const patternRegex = toRegExp(propertyNames.pattern);
360 if (!patternRegex.test(key)) {
361 const errorMessage = propertyNames.patternErrorMessage ||
362 nls.localize('validations.propertyNamePattern', 'Property name must match pattern `{0}`.', propertyNames.pattern);
363 errors.push(errorMessage + '\n');
364 propertyNamesErrorShown = true;
365 }
366 }
368 > if (properties && key in properties) {
369 > const errorMessage = getErrorsForSchema(properties[key], data);
370 > if (errorMessage) {
371 > errors.push(`${key}: ${errorMessage}\n`);
372 > }
373 > return;
374 > }
376 > if (patternProperties) {
377 > for (const pattern in patternProperties) {
378 > if (RegExp(pattern).test(key)) {
379 > const errorMessage = getErrorsForSchema(patternProperties[pattern], data);
380 > if (errorMessage) {
381 > errors.push(`${key}: ${errorMessage}\n`);
382 > }
383 > return;
384 > }
385 > }
386 > }
387 >
388 > if (additionalProperties === false) {
389 > errors.push(nls.localize('validations.objectPattern', 'Property {0} is not allowed.\n', key));
390 > } else if (typeof additionalProperties === 'object') {
391 > const errorMessage = getErrorsForSchema(additionalProperties, data);
392 > if (errorMessage) {
393 > errors.push(`${key}: ${errorMessage}\n`);
394 > }
395 > }
397 > }
398 >
399 > if (errors.length) {
400 > return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
401 > }
402 >
403 > return '';
404 > };
405 > }
407 > return null;
408 > }
410 > /**
411 > * Validates a single property name against the propertyNames.pattern schema.
412 > * Returns true if the key is valid, false otherwise.
413 > */
414 > export function validatePropertyName(propertyNames: IConfigurationPropertySchema['propertyNames'], key: string): boolean {
415 if (!propertyNames?.pattern) {
416 return true;
417 }
418 const patternRegex = toRegExp(propertyNames.pattern);
419 return patternRegex.test(key);
420 }
422 > function getErrorsForSchema(propertySchema: IConfigurationPropertySchema, data: any): string | null { preferencesValidation.ts ×1
423 > const validator = createValidator(propertySchema);
424 > const errorMessage = validator(data);
425 > return errorMessage;
426 > }