@mapbox/mapbox-gl-style-spec 14.15.0-beta.2 → 14.15.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 (74) hide show
  1. package/deref.ts +3 -0
  2. package/diff.ts +63 -0
  3. package/dist/index.cjs +722 -47
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +85 -6
  6. package/dist/index.es.js +722 -47
  7. package/dist/index.es.js.map +1 -1
  8. package/expression/compound_expression.ts +4 -0
  9. package/expression/definitions/assertion.ts +7 -0
  10. package/expression/definitions/case.ts +2 -1
  11. package/expression/definitions/coalesce.ts +4 -0
  12. package/expression/definitions/coercion.ts +12 -0
  13. package/expression/definitions/collator.ts +8 -5
  14. package/expression/definitions/comparison.ts +12 -5
  15. package/expression/definitions/config.ts +12 -0
  16. package/expression/definitions/distance.ts +13 -2
  17. package/expression/definitions/format.ts +10 -0
  18. package/expression/definitions/image.ts +3 -0
  19. package/expression/definitions/in.ts +5 -0
  20. package/expression/definitions/index.ts +62 -10
  21. package/expression/definitions/index_of.ts +6 -0
  22. package/expression/definitions/interpolate.ts +5 -1
  23. package/expression/definitions/length.ts +2 -0
  24. package/expression/definitions/let.ts +1 -0
  25. package/expression/definitions/match.ts +5 -0
  26. package/expression/definitions/number_format.ts +7 -0
  27. package/expression/definitions/slice.ts +4 -0
  28. package/expression/definitions/within.ts +1 -0
  29. package/expression/index.ts +28 -19
  30. package/expression/parsing_context.ts +2 -0
  31. package/expression/types/image_variant.ts +3 -0
  32. package/expression/types.ts +1 -2
  33. package/expression/values.ts +1 -0
  34. package/feature_filter/convert.ts +9 -1
  35. package/feature_filter/index.ts +41 -1
  36. package/format.ts +5 -0
  37. package/function/convert.ts +5 -0
  38. package/function/index.ts +79 -25
  39. package/group_by_layout.ts +4 -5
  40. package/migrate/v8.ts +42 -3
  41. package/migrate/v9.ts +5 -0
  42. package/migrate.ts +1 -0
  43. package/package.json +1 -1
  44. package/read_style.ts +2 -0
  45. package/reference/v8.json +457 -12
  46. package/rollup.config.js +1 -0
  47. package/test.js +4 -0
  48. package/types.ts +74 -5
  49. package/util/geometry_util.ts +4 -0
  50. package/validate/validate.ts +1 -0
  51. package/validate/validate_appearance.ts +101 -0
  52. package/validate/validate_array.ts +1 -0
  53. package/validate/validate_expression.ts +48 -3
  54. package/validate/validate_filter.ts +5 -3
  55. package/validate/validate_fog.ts +6 -0
  56. package/validate/validate_function.ts +2 -0
  57. package/validate/validate_iconset.ts +1 -0
  58. package/validate/validate_import.ts +2 -2
  59. package/validate/validate_layer.ts +37 -4
  60. package/validate/validate_light.ts +6 -0
  61. package/validate/validate_lights.ts +9 -0
  62. package/validate/validate_model.ts +1 -2
  63. package/validate/validate_number.ts +2 -0
  64. package/validate/validate_object.ts +7 -0
  65. package/validate/validate_projection.ts +2 -0
  66. package/validate/validate_property.ts +15 -4
  67. package/validate/validate_rain.ts +5 -0
  68. package/validate/validate_snow.ts +5 -0
  69. package/validate/validate_source.ts +12 -0
  70. package/validate/validate_style.ts +1 -0
  71. package/validate/validate_terrain.ts +6 -0
  72. package/validate_mapbox_api_supported.ts +1 -0
  73. package/visit.ts +2 -0
  74. package/util/extend.ts +0 -9
package/types.ts CHANGED
@@ -130,6 +130,35 @@ export type ModelsSpecification = {
130
130
  [_: string]: ModelSpecification
131
131
  };
132
132
 
133
+ export type ModelNodeOverrideSpecification = {
134
+ "orientation"?: [number, number, number]
135
+ };
136
+ export type ModelNodeOverridesSpecification = {
137
+ [_: string]: ModelNodeOverrideSpecification
138
+ };
139
+ export type ModelMaterialOverrideSpecification = {
140
+ "model-color"?: ColorSpecification,
141
+ "model-color-mix-intensity"?: number,
142
+ "model-opacity"?: number,
143
+ "model-emissive-strength"?: number
144
+ };
145
+ export type ModelMaterialOverridesSpecification = {
146
+ [_: string]: ModelMaterialOverrideSpecification
147
+ };
148
+ export type ModelSourceModelsSpecification = {
149
+ [_: string]: ModelSourceModelSpecification
150
+ };
151
+ export type ModelSourceModelSpecification = {
152
+ "uri": string,
153
+ "position"?: [number, number],
154
+ "orientation"?: [number, number, number],
155
+ "nodeOverrides"?: ModelNodeOverridesSpecification,
156
+ "materialOverrides"?: ModelMaterialOverridesSpecification,
157
+ "nodeOverrideNames"?: Array<string>,
158
+ "materialOverrideNames"?: Array<string>,
159
+ "featureProperties"?: unknown
160
+ };
161
+
133
162
  export type IconsetsSpecification = {
134
163
  [_: string]: IconsetSpecification
135
164
  };
@@ -383,7 +412,7 @@ export type SelectorPropertySpecification = {
383
412
  };
384
413
 
385
414
  export type AppearanceSpecification = {
386
- "condition"?: ExpressionSpecification,
415
+ "condition"?: DataDrivenPropertyValueSpecification<boolean>,
387
416
  "name"?: string,
388
417
  "properties"?: unknown
389
418
  };
@@ -499,7 +528,8 @@ export type ModelSourceSpecification = {
499
528
  "type": "model" | "batched-model",
500
529
  "maxzoom"?: number,
501
530
  "minzoom"?: number,
502
- "tiles"?: Array<string>
531
+ "tiles"?: Array<string>,
532
+ "models"?: ModelSourceModelsSpecification
503
533
  };
504
534
 
505
535
  export type SourceSpecification =
@@ -1099,6 +1129,10 @@ export type BuildingLayerSpecification = {
1099
1129
  * @experimental This property is experimental and subject to change in future versions.
1100
1130
  */
1101
1131
  "building-facade-floors"?: DataDrivenPropertyValueSpecification<number>,
1132
+ /**
1133
+ * @experimental This property is experimental and subject to change in future versions.
1134
+ */
1135
+ "building-facade-unit-width"?: DataDrivenPropertyValueSpecification<number>,
1102
1136
  /**
1103
1137
  * @experimental This property is experimental and subject to change in future versions.
1104
1138
  */
@@ -1116,7 +1150,22 @@ export type BuildingLayerSpecification = {
1116
1150
  * @experimental This property is experimental and subject to change in future versions.
1117
1151
  */
1118
1152
  "building-base"?: DataDrivenPropertyValueSpecification<number>,
1119
- "building-base-transition"?: TransitionSpecification
1153
+ "building-base-transition"?: TransitionSpecification,
1154
+ /**
1155
+ * @experimental This property is experimental and subject to change in future versions.
1156
+ */
1157
+ "building-flood-light-wall-radius"?: DataDrivenPropertyValueSpecification<number>,
1158
+ "building-flood-light-wall-radius-transition"?: TransitionSpecification,
1159
+ /**
1160
+ * @experimental This property is experimental and subject to change in future versions.
1161
+ */
1162
+ "building-flood-light-ground-radius"?: DataDrivenPropertyValueSpecification<number>,
1163
+ "building-flood-light-ground-radius-transition"?: TransitionSpecification,
1164
+ /**
1165
+ * @experimental This property is experimental and subject to change in future versions.
1166
+ */
1167
+ "building-flip-roof-orientation"?: DataDrivenPropertyValueSpecification<boolean>,
1168
+ "building-flip-roof-orientation-transition"?: TransitionSpecification
1120
1169
  },
1121
1170
  "paint"?: {
1122
1171
  /**
@@ -1166,7 +1215,23 @@ export type BuildingLayerSpecification = {
1166
1215
  * @experimental This property is experimental and subject to change in future versions.
1167
1216
  */
1168
1217
  "building-facade-emissive-chance"?: PropertyValueSpecification<number>,
1169
- "building-cutoff-fade-range"?: ExpressionSpecification
1218
+ "building-cutoff-fade-range"?: ExpressionSpecification,
1219
+ /**
1220
+ * @experimental This property is experimental and subject to change in future versions.
1221
+ */
1222
+ "building-flood-light-color"?: PropertyValueSpecification<ColorSpecification>,
1223
+ "building-flood-light-color-transition"?: TransitionSpecification,
1224
+ "building-flood-light-color-use-theme"?: PropertyValueSpecification<string>,
1225
+ /**
1226
+ * @experimental This property is experimental and subject to change in future versions.
1227
+ */
1228
+ "building-flood-light-intensity"?: PropertyValueSpecification<number>,
1229
+ "building-flood-light-intensity-transition"?: TransitionSpecification,
1230
+ /**
1231
+ * @experimental This property is experimental and subject to change in future versions.
1232
+ */
1233
+ "building-flood-light-ground-attenuation"?: PropertyValueSpecification<number>,
1234
+ "building-flood-light-ground-attenuation-transition"?: TransitionSpecification
1170
1235
  },
1171
1236
  "appearances"?: Array<AppearanceSpecification>
1172
1237
  };
@@ -1363,7 +1428,11 @@ export type ModelLayerSpecification = {
1363
1428
  "model-height-based-emissive-strength-multiplier"?: DataDrivenPropertyValueSpecification<[number, number, number, number, number]>,
1364
1429
  "model-height-based-emissive-strength-multiplier-transition"?: TransitionSpecification,
1365
1430
  "model-cutoff-fade-range"?: ExpressionSpecification,
1366
- "model-front-cutoff"?: PropertyValueSpecification<[number, number, number]>
1431
+ "model-front-cutoff"?: PropertyValueSpecification<[number, number, number]>,
1432
+ /**
1433
+ * @experimental This property is experimental and subject to change in future versions.
1434
+ */
1435
+ "model-elevation-reference"?: "sea" | "ground" | "hd-road-markup" | ExpressionSpecification
1367
1436
  },
1368
1437
  "appearances"?: Array<AppearanceSpecification>
1369
1438
  };
@@ -16,6 +16,7 @@ function calculateSignedArea(ring: Ring): number {
16
16
  for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
17
17
  p1 = ring[i];
18
18
  p2 = ring[j];
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
19
20
  sum += (p2.x - p1.x) * (p1.y + p2.y);
20
21
  }
21
22
  return sum;
@@ -44,13 +45,16 @@ export function classifyRings(rings: Array<Ring>, maxRings: number): Array<Array
44
45
  if (ccw === undefined) ccw = area < 0;
45
46
 
46
47
  if (ccw === area < 0) {
48
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
47
49
  if (polygon) polygons.push(polygon);
48
50
  polygon = [rings[i]];
49
51
 
50
52
  } else {
53
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
51
54
  (polygon).push(rings[i]);
52
55
  }
53
56
  }
57
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
54
58
  if (polygon) polygons.push(polygon);
55
59
 
56
60
  // Earcut performance degrades with the # of rings in a polygon. For this
@@ -122,6 +122,7 @@ export default function validate(options: ValidatorOptions, arrayAsExpression: b
122
122
  }
123
123
 
124
124
  const errors = validateObject(Object.assign({}, options, {
125
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
125
126
  valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec
126
127
  }));
127
128
 
@@ -0,0 +1,101 @@
1
+ import validateObject from './validate_object';
2
+ import ValidationError from '../error/validation_error';
3
+ import validateProperty from './validate_property';
4
+ import {unbundle} from '../util/unbundle_jsonlint';
5
+ import validateExpression from './validate_expression';
6
+ import latest from '../reference/latest';
7
+
8
+ import type {StyleSpecification, LayerSpecification, AppearanceSpecification} from '../types';
9
+ import type {StyleReference} from '../reference/latest';
10
+
11
+ export type AppearanceValidatorOptions = {
12
+ key: string;
13
+ value: unknown;
14
+ style: Partial<StyleSpecification>;
15
+ styleSpec: StyleReference;
16
+ object?: object;
17
+ objectKey?: string;
18
+ layer: LayerSpecification;
19
+ layerType: string;
20
+ };
21
+
22
+ export default function validateAppearance(options: AppearanceValidatorOptions): Array<ValidationError> {
23
+ const {key, layer, layerType} = options;
24
+ const value = unbundle(options.value) as AppearanceSpecification;
25
+ const name = unbundle(value.name);
26
+ const condition = unbundle(value.condition);
27
+
28
+ const errors = validateObject({
29
+ key,
30
+ value,
31
+ valueSpec: options.styleSpec.appearance as object,
32
+ style: options.style,
33
+ styleSpec: options.styleSpec,
34
+ objectElementValidators: {
35
+ condition: (options) => validateCondition(Object.assign({layer, layerType}, options)),
36
+ properties: (options) => validateProperties(Object.assign({layer, layerType}, options)),
37
+ }
38
+ });
39
+
40
+ if (name !== 'hidden' && !condition) {
41
+ errors.push(new ValidationError(options.key, 'name', `Appearance with name different than "hidden" must have a condition`));
42
+ }
43
+
44
+ return errors;
45
+ }
46
+
47
+ function validateProperties(options: AppearanceValidatorOptions): Array<ValidationError> {
48
+ const errors: Array<ValidationError> = [];
49
+
50
+ const {styleSpec, layer, layerType} = options;
51
+
52
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
53
+ const paintProperties = styleSpec[`paint_${layerType}`];
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
55
+ const layoutProperties = styleSpec[`layout_${layerType}`];
56
+ const properties = options.object[options.objectKey] as object;
57
+
58
+ for (const propertyKey in properties) {
59
+ const propertyType =
60
+ propertyKey in paintProperties ? 'paint' :
61
+ propertyKey in layoutProperties ? 'layout' :
62
+ undefined;
63
+
64
+ if (!propertyType) {
65
+ errors.push(new ValidationError(options.key, propertyKey, `unknown property "${propertyKey}" for layer type "${layerType}"`));
66
+ continue;
67
+ }
68
+
69
+ const propertyValidationOptions = Object.assign({}, options, {
70
+ key: `${options.key}.${propertyKey}`,
71
+ object: properties,
72
+ objectKey: propertyKey,
73
+ layer,
74
+ layerType,
75
+ value: properties[propertyKey] as unknown,
76
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
77
+ valueSpec: (propertyType === 'paint' ? paintProperties[propertyKey] : layoutProperties[propertyKey]) as object,
78
+ });
79
+
80
+ errors.push(...validateProperty(propertyValidationOptions, propertyType));
81
+ }
82
+
83
+ return errors;
84
+ }
85
+
86
+ function validateCondition(options: AppearanceValidatorOptions): Array<ValidationError> {
87
+ const errors: Array<ValidationError> = [];
88
+
89
+ const appearance = options.object as AppearanceSpecification;
90
+ const condition = appearance.condition;
91
+
92
+ errors.push(...validateExpression({
93
+ key: options.key,
94
+ value: condition,
95
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
96
+ valueSpec: latest['appearance']['condition'],
97
+ expressionContext: 'appearance'
98
+ }));
99
+
100
+ return errors;
101
+ }
@@ -72,6 +72,7 @@ export default function validateArray(options: ArrayValidatorOptions): Validatio
72
72
  errors = errors.concat(validateArrayElement({
73
73
  array,
74
74
  arrayIndex: i,
75
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
75
76
  value: array[i],
76
77
  valueSpec: arrayElementSpec,
77
78
  style,
@@ -4,10 +4,10 @@ import {deepUnbundle} from '../util/unbundle_jsonlint';
4
4
  import {isStateConstant, isGlobalPropertyConstant, isFeatureConstant} from '../expression/is_constant';
5
5
  import CompoundExpression from '../expression/compound_expression';
6
6
 
7
+ import type {StylePropertySpecification} from '../../style-spec/style-spec';
7
8
  import type {Expression} from '../expression/expression';
8
9
  import type {StyleReference} from '../reference/latest';
9
10
  import type {StyleSpecification} from '../types';
10
- import type {StylePropertySpecification} from '../style-spec';
11
11
 
12
12
  export type ExpressionValidatorOptions = {
13
13
  key: string;
@@ -17,7 +17,7 @@ export type ExpressionValidatorOptions = {
17
17
  propertyType?: 'layout' | 'paint' | 'filter';
18
18
  style?: Partial<StyleSpecification>;
19
19
  styleSpec?: StyleReference;
20
- expressionContext?: 'property' | 'filter' | 'cluster-initial' | 'cluster-reduce' | 'cluster-map';
20
+ expressionContext?: 'property' | 'filter' | 'cluster-initial' | 'cluster-reduce' | 'cluster-map' | 'appearance';
21
21
  };
22
22
 
23
23
  export default function validateExpression(options: ExpressionValidatorOptions): ValidationError[] {
@@ -28,27 +28,37 @@ export default function validateExpression(options: ExpressionValidatorOptions):
28
28
  });
29
29
  }
30
30
 
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
32
32
  const expressionObj = (expression.value as any).expression || (expression.value as any)._styleExpression.expression;
33
33
 
34
34
  if (options.expressionContext === 'property' && (options.propertyKey === 'text-font') &&
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
35
36
  !expressionObj.outputDefined()) {
36
37
  return [new ValidationError(options.key, options.value, `Invalid data expression for "${options.propertyKey}". Output values must be contained as literals within the expression.`)];
37
38
  }
38
39
 
39
40
  if (options.expressionContext === 'property' && options.propertyType === 'layout' &&
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
40
42
  (!isStateConstant(expressionObj))) {
41
43
  return [new ValidationError(options.key, options.value, '"feature-state" data expressions are not supported with layout properties.')];
42
44
  }
43
45
 
44
46
  if (options.expressionContext === 'filter') {
47
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
45
48
  return disallowedFilterParameters(expressionObj, options);
46
49
  }
47
50
 
51
+ if (options.expressionContext === 'appearance') {
52
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
53
+ return checkDisallowedParameters(expressionObj, options);
54
+ }
55
+
48
56
  if (options.expressionContext && options.expressionContext.indexOf('cluster') === 0) {
57
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
49
58
  if (!isGlobalPropertyConstant(expressionObj, ['zoom', 'feature-state'])) {
50
59
  return [new ValidationError(options.key, options.value, '"zoom" and "feature-state" expressions are not supported with cluster properties.')];
51
60
  }
61
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
52
62
  if (options.expressionContext === 'cluster-initial' && !isFeatureConstant(expressionObj)) {
53
63
  return [new ValidationError(options.key, options.value, 'Feature data expressions are not supported with initial expression part of cluster properties.')];
54
64
  }
@@ -66,8 +76,11 @@ export function disallowedFilterParameters(e: Expression, options: any): Validat
66
76
  'distance-from-center'
67
77
  ]);
68
78
 
79
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
69
80
  if (options.valueSpec && options.valueSpec.expression) {
81
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
70
82
  for (const param of options.valueSpec.expression.parameters) {
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
71
84
  disallowedParameters.delete(param);
72
85
  }
73
86
  }
@@ -79,6 +92,7 @@ export function disallowedFilterParameters(e: Expression, options: any): Validat
79
92
 
80
93
  if (e instanceof CompoundExpression) {
81
94
  if (disallowedParameters.has(e.name)) {
95
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
82
96
  return [new ValidationError(options.key, options.value, `["${e.name}"] expression is not supported in a filter for a ${options.object.type} layer with id: ${options.object.id}`)];
83
97
  }
84
98
  }
@@ -88,3 +102,34 @@ export function disallowedFilterParameters(e: Expression, options: any): Validat
88
102
 
89
103
  return errors;
90
104
  }
105
+
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ function checkDisallowedParameters(e: Expression, options: any): ValidationError[] {
108
+ const allowedParameters = new Set<string>();
109
+
110
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
111
+ if (options.valueSpec && options.valueSpec.expression) {
112
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
113
+ for (const param of options.valueSpec.expression.parameters) {
114
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
115
+ allowedParameters.add(param);
116
+ }
117
+ }
118
+
119
+ if (allowedParameters.size === 0) {
120
+ return [];
121
+ }
122
+ const errors: ValidationError[] = [];
123
+
124
+ if (e instanceof CompoundExpression) {
125
+ if (!allowedParameters.has(e.name)) {
126
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
127
+ return [new ValidationError(options.key, options.value, `["${e.name}"] is not an allowed parameter`)];
128
+ }
129
+ }
130
+ e.eachChild((arg) => {
131
+ errors.push(...checkDisallowedParameters(arg, options));
132
+ });
133
+
134
+ return errors;
135
+ }
@@ -3,7 +3,6 @@ import validateExpression from './validate_expression';
3
3
  import validateEnum from './validate_enum';
4
4
  import {getType, isString, isNumber, isBoolean} from '../util/get_type';
5
5
  import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint';
6
- import extend from '../util/extend';
7
6
  import {isExpressionFilter} from '../feature_filter/index';
8
7
 
9
8
  import type {StyleReference} from '../reference/latest';
@@ -26,8 +25,9 @@ export default function validateFilter(options: FilterValidatorOptions): Validat
26
25
  // We default to a layerType of `fill` because that points to a non-dynamic filter definition within the style-spec.
27
26
  const layerType = options.layerType || 'fill';
28
27
 
29
- return validateExpression(extend({}, options, {
30
- expressionContext: 'filter',
28
+ return validateExpression(Object.assign({}, options, {
29
+ expressionContext: 'filter' as const,
30
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
31
31
  valueSpec: options.styleSpec[`filter_${layerType}`]
32
32
  }));
33
33
  } else {
@@ -51,6 +51,7 @@ function validateNonExpressionFilter(options: FilterValidatorOptions): Validatio
51
51
  let errors: ValidationError[] = validateEnum({
52
52
  key: `${key}[0]`,
53
53
  value: value[0],
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
54
55
  valueSpec: styleSpec.filter_operator
55
56
  });
56
57
 
@@ -83,6 +84,7 @@ function validateNonExpressionFilter(options: FilterValidatorOptions): Validatio
83
84
  errors = errors.concat(validateEnum({
84
85
  key: `${key}[${i}]`,
85
86
  value: value[i],
87
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
86
88
  valueSpec: styleSpec.geometry_type
87
89
  }));
88
90
  } else if (!isString(value[i]) && !isNumber(value[i]) && !isBoolean(value[i])) {
@@ -16,6 +16,7 @@ export default function validateFog(options: FogValidatorOptions): ValidationErr
16
16
  const fog = options.value;
17
17
  const style = options.style;
18
18
  const styleSpec = options.styleSpec;
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
19
20
  const fogSpec = styleSpec.fog;
20
21
 
21
22
  if (fog === undefined) {
@@ -31,6 +32,7 @@ export default function validateFog(options: FogValidatorOptions): ValidationErr
31
32
  const transitionMatch = key.match(/^(.*)-transition$/);
32
33
  const useThemeMatch = key.match(/^(.*)-use-theme$/);
33
34
 
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
34
36
  if (useThemeMatch && fogSpec[useThemeMatch[1]]) {
35
37
  errors = errors.concat(validate({
36
38
  key,
@@ -39,18 +41,22 @@ export default function validateFog(options: FogValidatorOptions): ValidationErr
39
41
  style,
40
42
  styleSpec
41
43
  }));
44
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
42
45
  } else if (transitionMatch && fogSpec[transitionMatch[1]] && fogSpec[transitionMatch[1]].transition) {
43
46
  errors = errors.concat(validate({
44
47
  key,
45
48
  value: fog[key],
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46
50
  valueSpec: styleSpec.transition,
47
51
  style,
48
52
  styleSpec
49
53
  }));
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
50
55
  } else if (fogSpec[key]) {
51
56
  errors = errors.concat(validate({
52
57
  key,
53
58
  value: fog[key],
59
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
54
60
  valueSpec: fogSpec[key],
55
61
  style,
56
62
  styleSpec
@@ -17,6 +17,7 @@ import type {StyleSpecification} from '../types';
17
17
  import type {StylePropertySpecification} from '../style-spec';
18
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
21
  const stops = value['stops'];
21
22
  return Array.isArray(stops) && Array.isArray(stops[0]) && isObject(stops[0][0]);
22
23
  }
@@ -51,6 +52,7 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
51
52
  const errors = validateObject({
52
53
  key: options.key,
53
54
  value: options.value,
55
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
54
56
  valueSpec: options.styleSpec.function,
55
57
  style: options.style,
56
58
  styleSpec: options.styleSpec,
@@ -38,6 +38,7 @@ export default function validateIconset(options: IconsetValidatorOptions): Valid
38
38
  errors = errors.concat(validateObject({
39
39
  key,
40
40
  value: iconset,
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
41
42
  valueSpec: styleSpec[`iconset_${type}`],
42
43
  style,
43
44
  styleSpec
@@ -1,4 +1,3 @@
1
- import extend from '../util/extend';
2
1
  import validateStyle from './validate_style';
3
2
  import validateObject from './validate_object';
4
3
  import ValidationError from '../error/validation_error';
@@ -31,8 +30,9 @@ export default function validateImport(options: ImportValidatorOptions): Validat
31
30
  enumerable: false
32
31
  });
33
32
 
34
- let errors = validateObject(extend({}, options, {
33
+ let errors = validateObject(Object.assign({}, options, {
35
34
  value: importSpec,
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
36
36
  valueSpec: styleSpec.import
37
37
  }));
38
38
 
@@ -1,11 +1,12 @@
1
1
  import ValidationError from '../error/validation_error';
2
2
  import {unbundle} from '../util/unbundle_jsonlint';
3
+ import validateArray from './validate_array';
3
4
  import validateObject from './validate_object';
4
5
  import validateFilter from './validate_filter';
6
+ import validateAppearance, {type AppearanceValidatorOptions} from './validate_appearance';
5
7
  import validatePaintProperty from './validate_paint_property';
6
8
  import validateLayoutProperty from './validate_layout_property';
7
9
  import validateSpec from './validate';
8
- import extend from '../util/extend';
9
10
  import {isObject, isString} from '../util/get_type';
10
11
 
11
12
  import type {StyleReference} from '../reference/latest';
@@ -64,9 +65,11 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
64
65
  if (!parent) {
65
66
  if (typeof ref === 'string')
66
67
  errors.push(new ValidationError(key, layer.ref, `ref layer "${ref}" not found`));
68
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
67
69
  } else if (parent.ref) {
68
70
  errors.push(new ValidationError(key, layer.ref, 'ref cannot reference another ref layer'));
69
71
  } else {
72
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
70
73
  type = unbundle(parent.type) as string;
71
74
  }
72
75
  } else if (!(type === 'background' || type === 'sky' || type === 'slot')) {
@@ -101,6 +104,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
101
104
  errors = errors.concat(validateObject({
102
105
  key,
103
106
  value: layer,
107
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
104
108
  valueSpec: styleSpec.layer,
105
109
  style: options.style,
106
110
  styleSpec: options.styleSpec,
@@ -114,6 +118,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
114
118
  return validateSpec({
115
119
  key: `${key}.type`,
116
120
  value: layer.type,
121
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
117
122
  valueSpec: styleSpec.layer.type,
118
123
  style: options.style,
119
124
  styleSpec: options.styleSpec,
@@ -122,7 +127,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
122
127
  });
123
128
  },
124
129
  filter(options) {
125
- return validateFilter(extend({layerType: type}, options));
130
+ return validateFilter(Object.assign({layerType: type}, options));
126
131
  },
127
132
  layout(options) {
128
133
  return validateObject({
@@ -134,7 +139,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
134
139
  styleSpec: options.styleSpec,
135
140
  objectElementValidators: {
136
141
  '*'(options) {
137
- return validateLayoutProperty(extend({layerType: type}, options));
142
+ return validateLayoutProperty(Object.assign({layerType: type}, options));
138
143
  }
139
144
  }
140
145
  });
@@ -149,10 +154,38 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
149
154
  styleSpec: options.styleSpec,
150
155
  objectElementValidators: {
151
156
  '*'(options) {
152
- return validatePaintProperty(extend({layerType: type, layer}, options));
157
+ return validatePaintProperty(Object.assign({layerType: type, layer}, options));
153
158
  }
154
159
  }
155
160
  });
161
+ },
162
+ appearances(options) {
163
+ const validationErrors = validateArray({
164
+ key: options.key,
165
+ value: options.value,
166
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
167
+ valueSpec: options.valueSpec,
168
+ style: options.style,
169
+ styleSpec: options.styleSpec,
170
+ arrayElementValidator: (options) => validateAppearance(Object.assign({layerType: type, layer}, options) as AppearanceValidatorOptions)
171
+ });
172
+ // Check non-repeated names on a given layer
173
+ const appearances = Array.isArray(options.value) ? options.value : [];
174
+ const dedupedNames = new Set<string>();
175
+ appearances.forEach((a, index) => {
176
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
177
+ const name: string | undefined = unbundle(a.name) as string | undefined;
178
+ if (name) {
179
+ if (dedupedNames.has(name)) {
180
+ const layerId = unbundle((layer as LayerSpecification).id) as string;
181
+ validationErrors.push(new ValidationError(options.key, name, `Duplicated appearance name "${name}" for layer "${layerId}"`));
182
+ } else {
183
+ dedupedNames.add(name);
184
+ }
185
+ }
186
+ });
187
+
188
+ return validationErrors;
156
189
  }
157
190
  }
158
191
  }));
@@ -15,6 +15,7 @@ type LightValidatorOptions = {
15
15
  export default function validateLight(options: LightValidatorOptions): ValidationError[] {
16
16
  const light = options.value;
17
17
  const styleSpec = options.styleSpec;
18
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
18
19
  const lightSpec = styleSpec.light;
19
20
  const style = options.style;
20
21
 
@@ -31,6 +32,7 @@ export default function validateLight(options: LightValidatorOptions): Validatio
31
32
  const transitionMatch = key.match(/^(.*)-transition$/);
32
33
  const useThemeMatch = key.match(/^(.*)-use-theme$/);
33
34
 
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
34
36
  if (useThemeMatch && lightSpec[useThemeMatch[1]]) {
35
37
  errors = errors.concat(validate({
36
38
  key,
@@ -39,18 +41,22 @@ export default function validateLight(options: LightValidatorOptions): Validatio
39
41
  style,
40
42
  styleSpec
41
43
  }));
44
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
42
45
  } else if (transitionMatch && lightSpec[transitionMatch[1]] && lightSpec[transitionMatch[1]].transition) {
43
46
  errors = errors.concat(validate({
44
47
  key,
45
48
  value: light[key],
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46
50
  valueSpec: styleSpec.transition,
47
51
  style,
48
52
  styleSpec
49
53
  }));
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
50
55
  } else if (lightSpec[key]) {
51
56
  errors = errors.concat(validate({
52
57
  key,
53
58
  value: light[key],
59
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
54
60
  valueSpec: lightSpec[key],
55
61
  style,
56
62
  styleSpec