@mapbox/mapbox-gl-style-spec 14.28.0 → 14.29.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 (63) hide show
  1. package/composite.ts +5 -5
  2. package/diff.ts +38 -40
  3. package/dist/index.cjs +362 -238
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +101 -13
  6. package/dist/index.es.js +362 -238
  7. package/dist/index.es.js.map +1 -1
  8. package/expression/compound_expression.ts +6 -6
  9. package/expression/definitions/assertion.ts +9 -12
  10. package/expression/definitions/at.ts +1 -1
  11. package/expression/definitions/at_interpolated.ts +1 -1
  12. package/expression/definitions/case.ts +1 -2
  13. package/expression/definitions/coalesce.ts +1 -1
  14. package/expression/definitions/coercion.ts +10 -12
  15. package/expression/definitions/collator.ts +8 -8
  16. package/expression/definitions/comparison.ts +0 -1
  17. package/expression/definitions/distance.ts +135 -73
  18. package/expression/definitions/format.ts +10 -13
  19. package/expression/definitions/image.ts +8 -8
  20. package/expression/definitions/index.ts +92 -90
  21. package/expression/definitions/interpolate.ts +18 -19
  22. package/expression/definitions/let.ts +1 -2
  23. package/expression/definitions/literal.ts +1 -1
  24. package/expression/definitions/match.ts +7 -8
  25. package/expression/definitions/number_format.ts +7 -8
  26. package/expression/definitions/step.ts +11 -11
  27. package/expression/definitions/within.ts +23 -24
  28. package/expression/evaluation_context.ts +4 -4
  29. package/expression/expression.ts +1 -1
  30. package/expression/index.ts +43 -21
  31. package/expression/parsing_context.ts +2 -2
  32. package/expression/stops.ts +2 -2
  33. package/expression/values.ts +2 -2
  34. package/feature_filter/index.ts +3 -3
  35. package/function/convert.ts +8 -8
  36. package/function/index.ts +4 -2
  37. package/group_by_layout.ts +6 -4
  38. package/package.json +1 -1
  39. package/read_style.ts +2 -2
  40. package/reference/v8.json +86 -5
  41. package/types/config_options.ts +1 -1
  42. package/types.ts +16 -6
  43. package/util/properties.ts +1 -1
  44. package/validate/validate.ts +15 -5
  45. package/validate/validate_appearance.ts +7 -7
  46. package/validate/validate_array.ts +7 -7
  47. package/validate/validate_enum.ts +2 -3
  48. package/validate/validate_fog.ts +2 -2
  49. package/validate/validate_function.ts +14 -13
  50. package/validate/validate_layer.ts +10 -9
  51. package/validate/validate_light.ts +2 -2
  52. package/validate/validate_lights.ts +3 -3
  53. package/validate/validate_number.ts +10 -7
  54. package/validate/validate_object.ts +11 -10
  55. package/validate/validate_option.ts +24 -9
  56. package/validate/validate_property.ts +4 -4
  57. package/validate/validate_rain.ts +1 -1
  58. package/validate/validate_snow.ts +1 -1
  59. package/validate/validate_style.ts +2 -1
  60. package/validate/validate_terrain.ts +2 -2
  61. package/validate_mapbox_api_supported.ts +5 -6
  62. package/validate_style.min.ts +2 -1
  63. package/visit.ts +11 -10
@@ -15,9 +15,9 @@ import {
15
15
  import type {StyleReference} from '../reference/latest';
16
16
  import type {StyleSpecification} from '../types';
17
17
  import type {StylePropertySpecification} from '../style-spec';
18
+ import type {ArraySpec} from './validate_array';
18
19
 
19
- function hasObjectStops(value: object): value is {stops: Array<Record<PropertyKey, unknown>>} {
20
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
20
+ function hasObjectStops(value: Record<PropertyKey, unknown>): value is {stops: Array<Record<PropertyKey, unknown>>} {
21
21
  const stops = value['stops'];
22
22
  return Array.isArray(stops) && Array.isArray(stops[0]) && isObject(stops[0][0]);
23
23
  }
@@ -25,11 +25,13 @@ function hasObjectStops(value: object): value is {stops: Array<Record<PropertyKe
25
25
  export type FunctionValidatorOptions = {
26
26
  key: string;
27
27
  value: unknown;
28
- valueSpec: unknown;
28
+ valueSpec?: unknown;
29
29
  style: Partial<StyleSpecification>;
30
30
  styleSpec: StyleReference;
31
31
  };
32
32
 
33
+ type FunctionValidator = (options: FunctionValidatorOptions) => ValidationError[];
34
+
33
35
  export default function validateFunction(options: FunctionValidatorOptions): ValidationError[] {
34
36
  const key = options.key;
35
37
  const value = options.value;
@@ -38,7 +40,7 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
38
40
  return [new ValidationError(key, value, `object expected, ${getType(value)} found`)];
39
41
  }
40
42
 
41
- const functionValueSpec = options.valueSpec;
43
+ const functionValueSpec = options.valueSpec as StylePropertySpecification;
42
44
  const functionType = unbundle(value.type);
43
45
  let stopKeyType: string | undefined;
44
46
  let stopDomainValues: Partial<Record<string | number, boolean>> = {};
@@ -70,14 +72,14 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
70
72
  errors.push(new ValidationError(options.key, options.value, 'missing required property "stops"'));
71
73
  }
72
74
 
73
- if (functionType === 'exponential' && (functionValueSpec as {expression?: unknown}).expression && !supportsInterpolation(functionValueSpec as StylePropertySpecification)) {
75
+ if (functionType === 'exponential' && functionValueSpec.expression && !supportsInterpolation(functionValueSpec)) {
74
76
  errors.push(new ValidationError(options.key, options.value, 'exponential functions not supported'));
75
77
  }
76
78
 
77
79
  if (options.styleSpec.$version >= 8) {
78
- if (isPropertyFunction && !supportsPropertyExpression(functionValueSpec as StylePropertySpecification)) {
80
+ if (isPropertyFunction && !supportsPropertyExpression(functionValueSpec)) {
79
81
  errors.push(new ValidationError(options.key, options.value, 'property functions not supported'));
80
- } else if (isZoomFunction && !supportsZoomExpression(functionValueSpec as StylePropertySpecification)) {
82
+ } else if (isZoomFunction && !supportsZoomExpression(functionValueSpec)) {
81
83
  errors.push(new ValidationError(options.key, options.value, 'zoom functions not supported'));
82
84
  }
83
85
  }
@@ -99,10 +101,10 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
99
101
  errors = errors.concat(validateArray({
100
102
  key: options.key,
101
103
  value,
102
- valueSpec: options.valueSpec,
104
+ valueSpec: options.valueSpec as ArraySpec,
103
105
  style: options.style,
104
106
  styleSpec: options.styleSpec,
105
- arrayElementValidator: validateFunctionStop
107
+ arrayElementValidator: validateFunctionStop as (...args: unknown[]) => ValidationError[]
106
108
  }));
107
109
 
108
110
  if (Array.isArray(value) && value.length === 0) {
@@ -157,7 +159,7 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
157
159
  valueSpec: {zoom: {}},
158
160
  style: options.style,
159
161
  styleSpec: options.styleSpec,
160
- objectElementValidators: {zoom: validateNumber, value: validateStopDomainValue}
162
+ objectElementValidators: {zoom: validateNumber as FunctionValidator, value: validateStopDomainValue}
161
163
  }));
162
164
  } else {
163
165
  errors = errors.concat(validateStopDomainValue({
@@ -182,7 +184,7 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
182
184
  }));
183
185
  }
184
186
 
185
- function validateStopDomainValue(options: FunctionValidatorOptions, stop?: unknown[]): ValidationError[] {
187
+ function validateStopDomainValue(options: FunctionValidatorOptions, stop?: object): ValidationError[] {
186
188
  const type = getType(options.value);
187
189
  const value = unbundle(options.value);
188
190
 
@@ -200,14 +202,13 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
200
202
 
201
203
  if (type !== 'number' && functionType !== 'categorical') {
202
204
  let message = `number expected, ${type} found`;
203
- if (supportsPropertyExpression(functionValueSpec as StylePropertySpecification) && functionType === undefined) {
205
+ if (supportsPropertyExpression(functionValueSpec) && functionType === undefined) {
204
206
  message += '\nIf you intended to use a categorical function, specify `"type": "categorical"`.';
205
207
  }
206
208
  return [new ValidationError(options.key, reportValue, message)];
207
209
  }
208
210
 
209
211
  if (functionType === 'categorical' && type === 'number' && (typeof value !== 'number' || !isFinite(value) || Math.floor(value) !== value)) {
210
- // eslint-disable-next-line @typescript-eslint/no-base-to-string -- value narrowed to number|string|boolean by the check above
211
212
  return [new ValidationError(options.key, reportValue, `integer expected, found ${String(value)}`)];
212
213
  }
213
214
 
@@ -11,6 +11,7 @@ import {isObject, isString} from '../util/get_type';
11
11
 
12
12
  import type {StyleReference} from '../reference/latest';
13
13
  import type {PropertyValidatorOptions} from './validate_property';
14
+ import type {ArraySpec} from './validate_array';
14
15
  import type {StyleSpecification, LayerSpecification, GeoJSONSourceSpecification} from '../types';
15
16
 
16
17
  type LayerValidatorOptions = {
@@ -43,7 +44,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
43
44
  if (layer.id) {
44
45
  const layerId = unbundle(layer.id) as string;
45
46
  for (let i = 0; i < options.arrayIndex; i++) {
46
- const otherLayer = style.layers[i];
47
+ const otherLayer = style.layers![i]!;
47
48
  if (unbundle(otherLayer.id) === layerId) {
48
49
  errors.push(new ValidationError(key, layer.id, `duplicate layer id "${layerId}", previously used at line ${(otherLayer.id as {__line__?: number}).__line__}`));
49
50
  }
@@ -59,7 +60,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
59
60
 
60
61
  let parent: LayerSpecification | undefined;
61
62
 
62
- style.layers.forEach((layer) => {
63
+ style.layers!.forEach((layer) => {
63
64
  if (unbundle(layer.id) === ref) parent = layer;
64
65
  });
65
66
 
@@ -94,10 +95,10 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
94
95
  errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\'.'));
95
96
  } else if (sourceType === 'raster-array' && !['raster', 'raster-particle'].includes(type)) {
96
97
  errors.push(new ValidationError(key, layer.source, `raster-array source can only be used with layer type \'raster\'.`));
97
- } else if (type === 'line' && layer.paint && layer.paint['line-gradient'] &&
98
+ } else if (type === 'line' && layer.paint && (layer.paint as Record<string, unknown>)['line-gradient'] &&
98
99
  (sourceType === 'geojson' && !(source as GeoJSONSourceSpecification).lineMetrics)) {
99
100
  errors.push(new ValidationError(key, layer, `layer "${layer.id as string}" specifies a line-gradient, which requires the GeoJSON source to have \`lineMetrics\` enabled.`));
100
- } else if (type === 'line' && layer.paint && layer.paint['line-trim-offset'] &&
101
+ } else if (type === 'line' && layer.paint && (layer.paint as Record<string, unknown>)['line-trim-offset'] &&
101
102
  (sourceType === 'geojson' && !(source as GeoJSONSourceSpecification).lineMetrics)) {
102
103
  errors.push(new ValidationError(key, layer, `layer "${layer.id as string}" specifies a line-trim-offset, which requires the GeoJSON source to have \`lineMetrics\` enabled.`));
103
104
  } else if (type === 'raster-particle' && sourceType !== 'raster-array') {
@@ -143,8 +144,8 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
143
144
  style: options.style,
144
145
  styleSpec: options.styleSpec,
145
146
  objectElementValidators: {
146
- '*'(options: PropertyValidatorOptions) {
147
- return validateLayoutProperty({layerType: type, ...options});
147
+ '*'(options) {
148
+ return validateLayoutProperty({layerType: type, ...options} as PropertyValidatorOptions);
148
149
  }
149
150
  }
150
151
  });
@@ -158,8 +159,8 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
158
159
  style: options.style,
159
160
  styleSpec: options.styleSpec,
160
161
  objectElementValidators: {
161
- '*'(options: PropertyValidatorOptions) {
162
- return validatePaintProperty({layerType: type, layer, ...options});
162
+ '*'(options) {
163
+ return validatePaintProperty({layerType: type, layer, ...options} as PropertyValidatorOptions);
163
164
  }
164
165
  }
165
166
  });
@@ -169,7 +170,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
169
170
  key: options.key,
170
171
  value: options.value,
171
172
 
172
- valueSpec: options.valueSpec,
173
+ valueSpec: options.valueSpec as ArraySpec,
173
174
  style: options.style,
174
175
  styleSpec: options.styleSpec,
175
176
  arrayElementValidator: (options) => validateAppearance(({layerType: type, layer, ...(options as object)}) as AppearanceValidatorOptions)
@@ -34,7 +34,7 @@ export default function validateLight(options: LightValidatorOptions): Validatio
34
34
  const useThemeMatch = key.match(USE_THEME_KEY_RE);
35
35
 
36
36
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
37
- if (useThemeMatch && lightSpec[useThemeMatch[1]]) {
37
+ if (useThemeMatch && lightSpec[useThemeMatch[1]!]) {
38
38
  errors = errors.concat(validate({
39
39
  key,
40
40
  value: light[key],
@@ -43,7 +43,7 @@ export default function validateLight(options: LightValidatorOptions): Validatio
43
43
  styleSpec
44
44
  }));
45
45
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
46
- } else if (transitionMatch && lightSpec[transitionMatch[1]] && lightSpec[transitionMatch[1]].transition) {
46
+ } else if (transitionMatch && lightSpec[transitionMatch[1]!] && lightSpec[transitionMatch[1]!]!.transition) {
47
47
  errors = errors.concat(validate({
48
48
  key,
49
49
  value: light[key],
@@ -49,7 +49,7 @@ export default function validateLights(options: LightsValidatorOptions): Validat
49
49
  if (lights) {
50
50
  for (let i = 0; i < options.arrayIndex; i++) {
51
51
  const lightType = unbundle(light.type);
52
- const otherLight = lights[i];
52
+ const otherLight = lights[i]!;
53
53
  if (unbundle(otherLight.type) === lightType) {
54
54
  errors.push(new ValidationError(key, light.id, `duplicate light type "${light.type}", previously defined at line ${(otherLight.id as {__line__?: number}).__line__}`));
55
55
  }
@@ -77,7 +77,7 @@ export default function validateLights(options: LightsValidatorOptions): Validat
77
77
  const useThemeMatch = propertyKey.match(USE_THEME_KEY_RE);
78
78
 
79
79
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
80
- if (useThemeMatch && lightPropertySpec[useThemeMatch[1]]) {
80
+ if (useThemeMatch && lightPropertySpec[useThemeMatch[1]!]) {
81
81
  errors = errors.concat(validate({
82
82
  key,
83
83
  value: properties[propertyKey],
@@ -86,7 +86,7 @@ export default function validateLights(options: LightsValidatorOptions): Validat
86
86
  styleSpec
87
87
  }));
88
88
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
89
- } else if (transitionMatch && lightPropertySpec[transitionMatch[1]] && lightPropertySpec[transitionMatch[1]].transition) {
89
+ } else if (transitionMatch && lightPropertySpec[transitionMatch[1]!] && lightPropertySpec[transitionMatch[1]!].transition) {
90
90
  errors = errors.concat(validate({
91
91
  key,
92
92
  value: light[key],
@@ -5,10 +5,15 @@ import type {StyleReference} from '../reference/latest';
5
5
  import type {StyleSpecification} from '../types';
6
6
  import type {NumberPropertySpecification} from '../style-spec';
7
7
 
8
+ type NumberValueSpecification = Omit<NumberPropertySpecification, 'minimum' | 'maximum'> & {
9
+ minimum?: number | number[];
10
+ maximum?: number | number[];
11
+ };
12
+
8
13
  type NumberValidatorOptions = {
9
14
  key: string;
10
15
  value: unknown;
11
- valueSpec: NumberPropertySpecification;
16
+ valueSpec: NumberValueSpecification;
12
17
  style: Partial<StyleSpecification>;
13
18
  styleSpec: StyleReference;
14
19
  arrayIndex: number;
@@ -29,11 +34,10 @@ export default function validateNumber(options: NumberValidatorOptions): Validat
29
34
  }
30
35
 
31
36
  if ('minimum' in valueSpec) {
32
- let specMin = valueSpec.minimum;
37
+ let specMin = valueSpec.minimum as number;
33
38
  if (Array.isArray(valueSpec.minimum)) {
34
39
  const i = options.arrayIndex;
35
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
36
- specMin = valueSpec.minimum[i];
40
+ specMin = valueSpec.minimum[i]!;
37
41
  }
38
42
  if (value < specMin) {
39
43
  return [new ValidationError(key, value, `${value} is less than the minimum value ${specMin}`)];
@@ -41,11 +45,10 @@ export default function validateNumber(options: NumberValidatorOptions): Validat
41
45
  }
42
46
 
43
47
  if ('maximum' in valueSpec) {
44
- let specMax = valueSpec.maximum;
48
+ let specMax = valueSpec.maximum as number;
45
49
  if (Array.isArray(valueSpec.maximum)) {
46
50
  const i = options.arrayIndex;
47
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
48
- specMax = valueSpec.maximum[i];
51
+ specMax = valueSpec.maximum[i]!;
49
52
  }
50
53
  if (value > specMax) {
51
54
  return [new ValidationError(key, value, `${value} is greater than the maximum value ${specMax}`)];
@@ -15,16 +15,18 @@ type ObjectElementValidatorOptions = {
15
15
  objectKey?: string;
16
16
  };
17
17
 
18
+ export type ObjectElementValidator = (options: ObjectElementValidatorOptions, object?: object) => ValidationError[];
19
+
18
20
  type ObjectValidatorOptions = {
19
21
  key: string;
20
22
  value: unknown;
21
- valueSpec?: object;
23
+ valueSpec?: Record<PropertyKey, unknown>;
22
24
  style: Partial<StyleSpecification>;
23
25
  styleSpec: StyleReference;
24
26
  object?: object;
25
27
  objectKey?: string;
26
28
  layer?: LayerSpecification;
27
- objectElementValidators?: Record<string, (options: ObjectElementValidatorOptions) => ValidationError[]>;
29
+ objectElementValidators?: Record<string, ObjectElementValidator>;
28
30
  };
29
31
 
30
32
  export default function validateObject(options: ObjectValidatorOptions): ValidationError[] {
@@ -47,25 +49,24 @@ export default function validateObject(options: ObjectValidatorOptions): Validat
47
49
  // inherited properties as if they were user-supplied.
48
50
  if (!Object.hasOwn(object, objectKey)) continue;
49
51
 
50
- const elementSpecKey = objectKey.split('.')[0]; // treat 'paint.*' as 'paint'
52
+ const elementSpecKey = objectKey.split('.')[0]!; // treat 'paint.*' as 'paint'
51
53
  // Object.hasOwn: a bare lookup like `elementSpecs[objectKey]` would
52
54
  // find inherited keys from Object.prototype (e.g. "__proto__",
53
55
  // "constructor", "toString") when the user-supplied key matches one,
54
56
  // returning a non-spec value that then fails as a validator.
55
57
  const hasSpec = Object.hasOwn(elementSpecs, elementSpecKey);
56
58
  const hasWildcardSpec = Object.hasOwn(elementSpecs, '*');
57
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
58
59
  const elementSpec = hasSpec ? elementSpecs[elementSpecKey] : (hasWildcardSpec ? elementSpecs['*'] : undefined);
59
60
 
60
- let validateElement: ((options: ObjectElementValidatorOptions, object?: unknown) => ValidationError[]) | undefined;
61
+ let validateElement: ObjectElementValidator | undefined;
61
62
  if (Object.hasOwn(elementValidators, elementSpecKey)) {
62
- validateElement = elementValidators[elementSpecKey];
63
+ validateElement = elementValidators[elementSpecKey]!;
63
64
  } else if (hasSpec) {
64
- validateElement = validateSpec;
65
+ validateElement = validateSpec as ObjectElementValidator;
65
66
  } else if (Object.hasOwn(elementValidators, '*')) {
66
- validateElement = elementValidators['*'];
67
+ validateElement = elementValidators['*']!;
67
68
  } else if (hasWildcardSpec) {
68
- validateElement = validateSpec;
69
+ validateElement = validateSpec as ObjectElementValidator;
69
70
  }
70
71
 
71
72
  if (!validateElement) {
@@ -91,7 +92,7 @@ export default function validateObject(options: ObjectValidatorOptions): Validat
91
92
  continue;
92
93
  }
93
94
 
94
- const elementSpec = elementSpecs[elementSpecKey] as {required?: boolean; default?: unknown};
95
+ const elementSpec = elementSpecs[elementSpecKey]! as {required?: boolean; default?: unknown};
95
96
  if (elementSpec.required && elementSpec['default'] === undefined && (!Object.hasOwn(object, elementSpecKey) || object[elementSpecKey] === undefined)) {
96
97
  errors.push(new ValidationError(key, object, `missing required property "${elementSpecKey}"`));
97
98
  }
@@ -5,6 +5,7 @@ import {isObject} from '../util/get_type';
5
5
 
6
6
  import type ValidationError from '../error/validation_error';
7
7
  import type {ValidatorOptions} from './validate';
8
+ import type {ObjectElementValidator} from './validate_object';
8
9
 
9
10
  // Validator for a single config option (one entry in `schema`). Wraps the
10
11
  // generic object validator to inherit the option's declared `type` when
@@ -19,18 +20,32 @@ export default function validateOption(options: ValidatorOptions): ValidationErr
19
20
  const isArrayOption = isObject(optionValue) && unbundle(optionValue.array) === true;
20
21
  const declaredType = !isArrayOption && isObject(optionValue) ? unbundle(optionValue.type) : undefined;
21
22
 
23
+ const validateDefault = (elementOptions: ValidatorOptions): ValidationError[] => {
24
+ const defaultValue = elementOptions.value;
25
+ if (isObject(defaultValue)) {
26
+ // A plain object default (e.g. a GeoJSON geometry for an
27
+ // `object`-typed option) isn't an expression or a legacy
28
+ // function spec -- validate it as a bare value like a runtime
29
+ // `config` value, instead of running it through
30
+ // validateFunction()/validateExpression() (see `validate()`).
31
+ return validateSpec({...elementOptions, valueSpec: {...elementOptions.valueSpec, type: '*', expression: undefined} as unknown as typeof elementOptions.valueSpec});
32
+ }
33
+ // Only propagate the declared type when the default is an
34
+ // expression (array-form). Primitive defaults keep the previous
35
+ // permissive validation, mirroring the runtime parser's narrowing
36
+ // in style.ts/parser.cpp.
37
+ if (declaredType && Array.isArray(defaultValue)) {
38
+ return validateSpec({...elementOptions, valueSpec: {...elementOptions.valueSpec, type: declaredType} as typeof elementOptions.valueSpec});
39
+ }
40
+ return validateSpec(elementOptions);
41
+ };
42
+
22
43
  return validateObject({
23
44
  ...options,
24
45
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
25
46
  valueSpec: styleSpec.option,
26
- objectElementValidators: declaredType ? {
27
- // Only propagate the declared type when the default is an
28
- // expression (array-form). Primitive defaults keep the previous
29
- // permissive validation, mirroring the runtime parser's narrowing
30
- // in style.ts/parser.cpp.
31
- default: (elementOptions: ValidatorOptions): ValidationError[] => (Array.isArray(elementOptions.value) ?
32
- validateSpec({...elementOptions, valueSpec: {...elementOptions.valueSpec, type: declaredType} as typeof elementOptions.valueSpec}) :
33
- validateSpec(elementOptions)),
34
- } : undefined,
47
+ objectElementValidators: {
48
+ default: validateDefault,
49
+ } as Record<string, ObjectElementValidator>,
35
50
  });
36
51
  }
@@ -31,13 +31,13 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
31
31
  const layer = options.layer;
32
32
  const styleSpec = options.styleSpec;
33
33
  const value = options.value;
34
- const propertyKey = options.objectKey;
34
+ const propertyKey = options.objectKey!;
35
35
  const layerSpec = styleSpec[`${propertyType}_${options.layerType}`] as Record<string, StylePropertySpecification> | undefined;
36
36
 
37
37
  if (!layerSpec) return [];
38
38
 
39
39
  const useThemeMatch = propertyKey.match(USE_THEME_KEY_RE);
40
- if (useThemeMatch && layerSpec[useThemeMatch[1]]) {
40
+ if (useThemeMatch && layerSpec[useThemeMatch[1]!]) {
41
41
  if (isExpression(deepUnbundle(value))) {
42
42
  const errors: ValidationError[] = [];
43
43
  return errors.concat(validate({
@@ -69,7 +69,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
69
69
  }
70
70
 
71
71
  const transitionMatch = propertyKey.match(TRANSITION_KEY_RE);
72
- if (propertyType === 'paint' && transitionMatch && layerSpec[transitionMatch[1]] && layerSpec[transitionMatch[1]].transition) {
72
+ if (propertyType === 'paint' && transitionMatch && layerSpec[transitionMatch[1]!] && layerSpec[transitionMatch[1]!]!.transition) {
73
73
  return validate({
74
74
  key,
75
75
  value,
@@ -85,7 +85,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
85
85
  return [new ValidationWarning(key, value, `unknown property "${propertyKey}"`)];
86
86
  }
87
87
 
88
- let tokenMatch: RegExpExecArray | undefined;
88
+ let tokenMatch: RegExpExecArray | null | undefined;
89
89
  if (isString(value) && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = TOKEN_PATTERN_RE.exec(value))) {
90
90
  const example = `\`{ "type": "identity", "property": ${tokenMatch ? JSON.stringify(tokenMatch[1]) : '"_"'} }\``;
91
91
  return [new ValidationError(
@@ -32,7 +32,7 @@ export default function validateRain(options: RainValidatorOptions): ValidationE
32
32
  for (const key in rain) {
33
33
  const transitionMatch = key.match(TRANSITION_KEY_RE);
34
34
 
35
- if (transitionMatch && rainSpec[transitionMatch[1]] && rainSpec[transitionMatch[1]].transition) {
35
+ if (transitionMatch && rainSpec[transitionMatch[1]!] && rainSpec[transitionMatch[1]!]!.transition) {
36
36
  errors = errors.concat(validate({
37
37
  key,
38
38
  value: rain[key],
@@ -32,7 +32,7 @@ export default function validateSnow(options: SnowValidatorOptions): ValidationE
32
32
  for (const key in snow) {
33
33
  const transitionMatch = key.match(TRANSITION_KEY_RE);
34
34
 
35
- if (transitionMatch && snowSpec[transitionMatch[1]] && snowSpec[transitionMatch[1]].transition) {
35
+ if (transitionMatch && snowSpec[transitionMatch[1]!] && snowSpec[transitionMatch[1]!]!.transition) {
36
36
  errors = errors.concat(validate({
37
37
  key,
38
38
  value: snow[key],
@@ -4,6 +4,7 @@ import validateGlyphsURL from './validate_glyphs_url';
4
4
 
5
5
  import type ValidationError from '../error/validation_error';
6
6
  import type {StyleReference} from '../reference/latest';
7
+ import type {StyleSpecification} from '../types';
7
8
 
8
9
  type StyleValidatorOptions = {
9
10
  key?: string;
@@ -20,7 +21,7 @@ export default function validateStyle(style: unknown, styleSpec: StyleReference
20
21
  '*': {type: '*'},
21
22
  },
22
23
  styleSpec,
23
- style,
24
+ style: style as Partial<StyleSpecification>,
24
25
  objectElementValidators: {
25
26
  glyphs: validateGlyphsURL
26
27
  }
@@ -36,7 +36,7 @@ export default function validateTerrain(options: TerrainValidatorOptions): Valid
36
36
  const useThemeMatch = key.match(USE_THEME_KEY_RE);
37
37
 
38
38
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
39
- if (useThemeMatch && terrainSpec[useThemeMatch[1]]) {
39
+ if (useThemeMatch && terrainSpec[useThemeMatch[1]!]) {
40
40
  errors = errors.concat(validate({
41
41
  key,
42
42
  value: terrain[key],
@@ -45,7 +45,7 @@ export default function validateTerrain(options: TerrainValidatorOptions): Valid
45
45
  styleSpec
46
46
  }));
47
47
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
48
- } else if (transitionMatch && terrainSpec[transitionMatch[1]] && terrainSpec[transitionMatch[1]].transition) {
48
+ } else if (transitionMatch && terrainSpec[transitionMatch[1]!] && terrainSpec[transitionMatch[1]!].transition) {
49
49
  errors = errors.concat(validate({
50
50
  key,
51
51
  value: terrain[key],
@@ -33,13 +33,13 @@ function isValid(value: string | null | undefined, regex: RegExp): boolean {
33
33
 
34
34
  function getSourceCount(source: SourceSpecification): number {
35
35
  if ('url' in source) {
36
- return source.url.split(',').length;
36
+ return source.url!.split(',').length;
37
37
  } else {
38
38
  return 0;
39
39
  }
40
40
  }
41
41
 
42
- function getAllowedKeyErrors(obj: object, keys: string[], path?: string | null): Array<ValidationError> {
42
+ function getAllowedKeyErrors(obj: Record<string, unknown>, keys: string[], path?: string | null): Array<ValidationError> {
43
43
  const allowed = new Set(keys);
44
44
  const errors: ValidationError[] = [];
45
45
  Object.keys(obj).forEach(k => {
@@ -87,21 +87,20 @@ function getSourcesErrors(sources: SourcesSpecification): {
87
87
  errors: Array<ValidationError>;
88
88
  sourcesCount: number;
89
89
  } {
90
- const errors = [];
90
+ const errors: ValidationError[] = [];
91
91
  let sourcesCount = 0;
92
92
 
93
93
  Object.keys(sources).forEach((s: string, i: number) => {
94
- const sourceErrors = getSourceErrors(sources[s], i);
94
+ const sourceErrors = getSourceErrors(sources[s]!, i);
95
95
 
96
96
  // If source has errors, skip counting
97
97
  if (!sourceErrors.length) {
98
- sourcesCount = sourcesCount + getSourceCount(sources[s]);
98
+ sourcesCount = sourcesCount + getSourceCount(sources[s]!);
99
99
  }
100
100
 
101
101
  errors.push(...sourceErrors);
102
102
  });
103
103
 
104
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
105
104
  return {errors, sourcesCount};
106
105
  }
107
106
 
@@ -23,7 +23,8 @@ export type ValidationError = {
23
23
  };
24
24
 
25
25
  export type ValidationErrors = ReadonlyArray<ValidationError>;
26
- export type Validator<T extends (...args: unknown[]) => unknown = (...args: unknown[]) => unknown> = (...args: Parameters<T>) => ValidationErrors;
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ export type Validator<T extends (...args: any[]) => unknown = (...args: unknown[]) => unknown> = (...args: Parameters<T>) => ValidationErrors;
27
28
 
28
29
  /**
29
30
  * Validate a Mapbox GL style against the style specification. This entrypoint,
package/visit.ts CHANGED
@@ -8,17 +8,18 @@ import type {
8
8
  PropertyValueSpecification
9
9
  } from './types';
10
10
 
11
- function getPropertyReference(propertyName: string): StylePropertySpecification {
11
+ type ReferenceSection = Record<string, StylePropertySpecification>;
12
+ type ReferenceWithSections = typeof Reference & Record<string, ReferenceSection>;
13
+
14
+ function getPropertyReference(propertyName: string): StylePropertySpecification | null {
12
15
  for (let i = 0; i < Reference.layout.length; i++) {
13
- for (const key in Reference[Reference.layout[i]]) {
14
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
15
- if (key === propertyName) return Reference[Reference.layout[i]][key] as StylePropertySpecification;
16
+ for (const key in (Reference as ReferenceWithSections)[Reference.layout[i]!]) {
17
+ if (key === propertyName) return (Reference as ReferenceWithSections)[Reference.layout[i]!]![key]!;
16
18
  }
17
19
  }
18
20
  for (let i = 0; i < Reference.paint.length; i++) {
19
- for (const key in Reference[Reference.paint[i]]) {
20
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
21
- if (key === propertyName) return Reference[Reference.paint[i]][key] as StylePropertySpecification;
21
+ for (const key in (Reference as ReferenceWithSections)[Reference.paint[i]!]) {
22
+ if (key === propertyName) return (Reference as ReferenceWithSections)[Reference.paint[i]!]![key]!;
22
23
  }
23
24
  }
24
25
 
@@ -27,7 +28,7 @@ function getPropertyReference(propertyName: string): StylePropertySpecification
27
28
 
28
29
  export function eachSource(style: StyleSpecification, callback: (_: SourceSpecification) => void) {
29
30
  for (const k in style.sources) {
30
- callback(style.sources[k]);
31
+ callback(style.sources[k]!);
31
32
  }
32
33
  }
33
34
 
@@ -59,14 +60,14 @@ export function eachProperty(
59
60
  ) {
60
61
  function inner(layer: LayerSpecification, propertyType: 'paint' | 'layout') {
61
62
  if (layer.type === 'slot' || layer.type === 'clip') return;
62
- const properties = layer[propertyType];
63
+ const properties: Record<string, PropertyValueSpecification<unknown>> | undefined = layer[propertyType];
63
64
  if (!properties) return;
64
65
  Object.keys(properties).forEach((key) => {
65
66
  callback({
66
67
  path: [layer.id, propertyType, key],
67
68
  key,
68
69
  value: properties[key],
69
- reference: getPropertyReference(key),
70
+ reference: getPropertyReference(key)!,
70
71
  set(x) {
71
72
  properties[key] = x;
72
73
  }