@mapbox/mapbox-gl-style-spec 14.14.0 → 14.15.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/index.cjs +325 -282
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.ts +12 -12
  4. package/dist/index.es.js +325 -282
  5. package/dist/index.es.js.map +1 -1
  6. package/error/validation_error.ts +3 -3
  7. package/expression/compound_expression.ts +1 -1
  8. package/expression/definitions/at.ts +3 -3
  9. package/expression/definitions/coercion.ts +2 -2
  10. package/expression/definitions/config.ts +1 -1
  11. package/expression/definitions/image.ts +1 -1
  12. package/expression/evaluation_context.ts +3 -1
  13. package/expression/index.ts +16 -7
  14. package/expression/parsing_context.ts +7 -3
  15. package/function/index.ts +3 -3
  16. package/package.json +1 -1
  17. package/reference/v8.json +1 -0
  18. package/types.ts +1 -0
  19. package/util/get_type.ts +29 -13
  20. package/validate/validate.ts +63 -42
  21. package/validate/validate_array.ts +36 -14
  22. package/validate/validate_boolean.ts +8 -7
  23. package/validate/validate_color.ts +8 -9
  24. package/validate/validate_enum.ts +15 -3
  25. package/validate/validate_expression.ts +17 -4
  26. package/validate/validate_filter.ts +22 -28
  27. package/validate/validate_fog.ts +17 -9
  28. package/validate/validate_formatted.ts +8 -3
  29. package/validate/validate_function.ts +49 -29
  30. package/validate/validate_glyphs_url.ts +9 -8
  31. package/validate/validate_iconset.ts +22 -5
  32. package/validate/validate_image.ts +8 -3
  33. package/validate/validate_import.ts +17 -2
  34. package/validate/validate_layer.ts +26 -15
  35. package/validate/validate_layout_property.ts +2 -2
  36. package/validate/validate_light.ts +17 -10
  37. package/validate/validate_lights.ts +29 -24
  38. package/validate/validate_model.ts +16 -11
  39. package/validate/validate_number.ts +21 -13
  40. package/validate/validate_object.ts +23 -10
  41. package/validate/validate_paint_property.ts +2 -2
  42. package/validate/validate_projection.ts +20 -10
  43. package/validate/validate_property.ts +14 -9
  44. package/validate/validate_rain.ts +17 -9
  45. package/validate/validate_snow.ts +17 -9
  46. package/validate/validate_source.ts +49 -20
  47. package/validate/validate_string.ts +9 -10
  48. package/validate/validate_style.ts +11 -14
  49. package/validate/validate_terrain.ts +22 -14
  50. package/validate_mapbox_api_supported.ts +3 -3
package/dist/index.cjs CHANGED
@@ -2872,6 +2872,7 @@
2872
2872
  doc: "Name of image in sprite to use for drawing an image background.",
2873
2873
  tokens: true,
2874
2874
  appearance: true,
2875
+ "use-theme": true,
2875
2876
  "sdk-support": {
2876
2877
  "basic functionality": {
2877
2878
  js: "0.10.0",
@@ -13878,19 +13879,40 @@
13878
13879
  }
13879
13880
 
13880
13881
  function getType(val) {
13881
- if (val instanceof Number) {
13882
- return 'number';
13883
- } else if (val instanceof String) {
13882
+ if (isString(val))
13884
13883
  return 'string';
13885
- } else if (val instanceof Boolean) {
13884
+ if (isNumber(val))
13885
+ return 'number';
13886
+ if (isBoolean(val))
13886
13887
  return 'boolean';
13887
- } else if (Array.isArray(val)) {
13888
+ if (Array.isArray(val))
13888
13889
  return 'array';
13889
- } else if (val === null) {
13890
+ if (val === null)
13890
13891
  return 'null';
13891
- } else {
13892
- return typeof val;
13892
+ if (isObject(val))
13893
+ return 'object';
13894
+ return typeof val;
13895
+ }
13896
+ function isObject(value) {
13897
+ if (value === null || value === void 0)
13898
+ return false;
13899
+ if (Array.isArray(value))
13900
+ return false;
13901
+ if (typeof value === 'function')
13902
+ return false;
13903
+ if (value instanceof String || value instanceof Number || value instanceof Boolean) {
13904
+ return false;
13893
13905
  }
13906
+ return typeof value === 'object';
13907
+ }
13908
+ function isString(value) {
13909
+ return typeof value === 'string' || value instanceof String;
13910
+ }
13911
+ function isNumber(value) {
13912
+ return typeof value === 'number' || value instanceof Number;
13913
+ }
13914
+ function isBoolean(value) {
13915
+ return typeof value === 'boolean' || value instanceof Boolean;
13894
13916
  }
13895
13917
 
13896
13918
  const types = {
@@ -13930,7 +13952,7 @@
13930
13952
  for (let i = 0; i < arrayLength; i++) {
13931
13953
  const member = args[1][i];
13932
13954
  let parsedMember;
13933
- if (getType(member) === 'array') {
13955
+ if (Array.isArray(member)) {
13934
13956
  parsedMember = context.parse(member, void 0, type.itemType);
13935
13957
  } else {
13936
13958
  const memberType = getType(member);
@@ -14044,7 +14066,7 @@
14044
14066
  'Polygon'
14045
14067
  ];
14046
14068
  class EvaluationContext {
14047
- constructor(scope, options) {
14069
+ constructor(scope, options, iconImageUseTheme) {
14048
14070
  this.globals = null;
14049
14071
  this.feature = null;
14050
14072
  this.featureState = null;
@@ -14056,6 +14078,7 @@
14056
14078
  this.featureDistanceData = null;
14057
14079
  this.scope = scope;
14058
14080
  this.options = options;
14081
+ this.iconImageUseTheme = iconImageUseTheme;
14059
14082
  }
14060
14083
  id() {
14061
14084
  return this.feature && this.feature.id !== void 0 ? this.feature.id : null;
@@ -14144,7 +14167,7 @@
14144
14167
  continue;
14145
14168
  overloadParams.push(params);
14146
14169
  overloadIndex++;
14147
- signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, void 0, context._scope, context.options);
14170
+ signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, void 0, context._scope, context.options, context.iconImageUseTheme);
14148
14171
  const parsedArgs = [];
14149
14172
  let argParseFailed = false;
14150
14173
  for (let i = 1; i < args.length; i++) {
@@ -16280,7 +16303,7 @@
16280
16303
  }
16281
16304
 
16282
16305
  class ParsingContext {
16283
- constructor(registry, path = [], expectedType, scope = new Scope(), errors = [], _scope, options) {
16306
+ constructor(registry, path = [], expectedType, scope = new Scope(), errors = [], _scope, options, iconImageUseTheme) {
16284
16307
  this.registry = registry;
16285
16308
  this.path = path;
16286
16309
  this.key = path.map(part => {
@@ -16294,6 +16317,7 @@
16294
16317
  this.expectedType = expectedType;
16295
16318
  this._scope = _scope;
16296
16319
  this.options = options;
16320
+ this.iconImageUseTheme = iconImageUseTheme;
16297
16321
  }
16298
16322
  /**
16299
16323
  * @param expr the JSON expression to parse
@@ -16356,7 +16380,7 @@
16356
16380
  }
16357
16381
  }
16358
16382
  if (!(parsed instanceof Literal) && parsed.type.kind !== 'resolvedImage' && isConstant(parsed)) {
16359
- const ec = new EvaluationContext(this._scope, this.options);
16383
+ const ec = new EvaluationContext(this._scope, this.options, this.iconImageUseTheme);
16360
16384
  try {
16361
16385
  parsed = new Literal(parsed.type, parsed.evaluate(ec));
16362
16386
  } catch (e) {
@@ -16390,7 +16414,7 @@
16390
16414
  let path = typeof index === 'number' ? this.path.concat(index) : this.path;
16391
16415
  path = typeof key === 'string' ? path.concat(key) : path;
16392
16416
  const scope = bindings ? this.scope.concat(bindings) : this.scope;
16393
- return new ParsingContext(this.registry, path, expectedType || null, scope, this.errors, this._scope, this.options);
16417
+ return new ParsingContext(this.registry, path, expectedType || null, scope, this.errors, this._scope, this.options, this.iconImageUseTheme);
16394
16418
  }
16395
16419
  /**
16396
16420
  * Push a parsing (or type checking) error into the `this.errors`
@@ -17042,13 +17066,13 @@
17042
17066
  const index = this.index.evaluate(ctx);
17043
17067
  const array2 = this.input.evaluate(ctx);
17044
17068
  if (index < 0) {
17045
- throw new RuntimeError(`Array index out of bounds: ${ index } < 0.`);
17069
+ throw new RuntimeError(`Array index out of bounds: negative index`);
17046
17070
  }
17047
17071
  if (index >= array2.length) {
17048
- throw new RuntimeError(`Array index out of bounds: ${ index } > ${ array2.length - 1 }.`);
17072
+ throw new RuntimeError(`Array index out of bounds: index exceeds array size`);
17049
17073
  }
17050
17074
  if (index !== Math.floor(index)) {
17051
- throw new RuntimeError(`Array index must be an integer, but found ${ index } instead. Use at-interpolated to retrieve interpolated result with a fractional index.`);
17075
+ throw new RuntimeError(`Array index must be an integer. Use at-interpolated for fractional indices`);
17052
17076
  }
17053
17077
  return array2[index];
17054
17078
  }
@@ -18703,7 +18727,7 @@
18703
18727
  return coalesce$1(evaluated, parameters.default, propertySpec.default);
18704
18728
  }
18705
18729
  function evaluateIntervalFunction(parameters, propertySpec, input) {
18706
- if (getType(input) !== 'number')
18730
+ if (!isNumber(input))
18707
18731
  return coalesce$1(parameters.default, propertySpec.default);
18708
18732
  const n = parameters.stops.length;
18709
18733
  if (n === 1)
@@ -18717,7 +18741,7 @@
18717
18741
  }
18718
18742
  function evaluateExponentialFunction(parameters, propertySpec, input) {
18719
18743
  const base = parameters.base !== void 0 ? parameters.base : 1;
18720
- if (getType(input) !== 'number')
18744
+ if (!isNumber(input))
18721
18745
  return coalesce$1(parameters.default, propertySpec.default);
18722
18746
  const n = parameters.stops.length;
18723
18747
  if (n === 1)
@@ -18774,10 +18798,10 @@
18774
18798
  }
18775
18799
 
18776
18800
  class StyleExpression {
18777
- constructor(expression, propertySpec, scope, options) {
18801
+ constructor(expression, propertySpec, scope, options, iconImageUseTheme) {
18778
18802
  this.expression = expression;
18779
18803
  this._warningHistory = {};
18780
- this._evaluator = new EvaluationContext(scope, options);
18804
+ this._evaluator = new EvaluationContext(scope, options, iconImageUseTheme);
18781
18805
  this._defaultValue = propertySpec ? getDefaultValue(propertySpec) : null;
18782
18806
  this._enumValues = propertySpec && propertySpec.type === 'enum' ? propertySpec.values : null;
18783
18807
  this.configDependencies = getConfigDependencies(expression);
@@ -18793,7 +18817,7 @@
18793
18817
  this._evaluator.featureDistanceData = featureDistanceData || null;
18794
18818
  return this.expression.evaluate(this._evaluator);
18795
18819
  }
18796
- evaluate(globals, feature, featureState, canonical, availableImages, formattedSection, featureTileCoord, featureDistanceData) {
18820
+ evaluate(globals, feature, featureState, canonical, availableImages, formattedSection, featureTileCoord, featureDistanceData, iconImageUseTheme) {
18797
18821
  this._evaluator.globals = globals;
18798
18822
  this._evaluator.feature = feature || null;
18799
18823
  this._evaluator.featureState = featureState || null;
@@ -18802,6 +18826,7 @@
18802
18826
  this._evaluator.formattedSection = formattedSection || null;
18803
18827
  this._evaluator.featureTileCoord = featureTileCoord || null;
18804
18828
  this._evaluator.featureDistanceData = featureDistanceData || null;
18829
+ this._evaluator.iconImageUseTheme = iconImageUseTheme || null;
18805
18830
  try {
18806
18831
  const val = this.expression.evaluate(this._evaluator);
18807
18832
  if (val === null || val === void 0 || typeof val === 'number' && val !== val) {
@@ -18825,13 +18850,13 @@
18825
18850
  function isExpression(expression) {
18826
18851
  return Array.isArray(expression) && expression.length > 0 && typeof expression[0] === 'string' && expression[0] in expressions;
18827
18852
  }
18828
- function createExpression(expression, propertySpec, scope, options) {
18829
- const parser = new ParsingContext(expressions, [], propertySpec ? getExpectedType(propertySpec) : void 0, void 0, void 0, scope, options);
18853
+ function createExpression(expression, propertySpec, scope, options, iconImageUseTheme) {
18854
+ const parser = new ParsingContext(expressions, [], propertySpec ? getExpectedType(propertySpec) : void 0, void 0, void 0, scope, options, iconImageUseTheme);
18830
18855
  const parsed = parser.parse(expression, void 0, void 0, void 0, propertySpec && propertySpec.type === 'string' ? { typeAnnotation: 'coerce' } : void 0);
18831
18856
  if (!parsed) {
18832
18857
  return error(parser.errors);
18833
18858
  }
18834
- return success(new StyleExpression(parsed, propertySpec, scope, options));
18859
+ return success(new StyleExpression(parsed, propertySpec, scope, options, iconImageUseTheme));
18835
18860
  }
18836
18861
  class ZoomConstantExpression {
18837
18862
  constructor(kind, expression, isLightConstant, isLineProgressConstant) {
@@ -18845,8 +18870,8 @@
18845
18870
  evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection) {
18846
18871
  return this._styleExpression.evaluateWithoutErrorHandling(globals, feature, featureState, canonical, availableImages, formattedSection);
18847
18872
  }
18848
- evaluate(globals, feature, featureState, canonical, availableImages, formattedSection) {
18849
- return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection);
18873
+ evaluate(globals, feature, featureState, canonical, availableImages, formattedSection, iconImageUseTheme) {
18874
+ return this._styleExpression.evaluate(globals, feature, featureState, canonical, availableImages, formattedSection, void 0, void 0, iconImageUseTheme);
18850
18875
  }
18851
18876
  }
18852
18877
  class ZoomDependentExpression {
@@ -18874,8 +18899,8 @@
18874
18899
  }
18875
18900
  }
18876
18901
  }
18877
- function createPropertyExpression(expression, propertySpec, scope, options) {
18878
- expression = createExpression(expression, propertySpec, scope, options);
18902
+ function createPropertyExpression(expression, propertySpec, scope, options, iconImageUseTheme) {
18903
+ expression = createExpression(expression, propertySpec, scope, options, iconImageUseTheme);
18879
18904
  if (expression.result === 'error') {
18880
18905
  return expression;
18881
18906
  }
@@ -18931,11 +18956,11 @@
18931
18956
  };
18932
18957
  }
18933
18958
  }
18934
- function normalizePropertyExpression(value, specification, scope, options) {
18959
+ function normalizePropertyExpression(value, specification, scope, options, iconImageUseTheme) {
18935
18960
  if (isFunction(value)) {
18936
18961
  return new StylePropertyFunction(value, specification);
18937
18962
  } else if (isExpression(value) || Array.isArray(value) && value.length > 0) {
18938
- const expression = createPropertyExpression(value, specification, scope, options);
18963
+ const expression = createPropertyExpression(value, specification, scope, options, iconImageUseTheme);
18939
18964
  if (expression.result === 'error') {
18940
18965
  throw new Error(expression.value.map(err => `${ err.key }: ${ err.message }`).join(', '));
18941
18966
  }
@@ -20601,58 +20626,12 @@ ${ JSON.stringify(filterExp, null, 2) }
20601
20626
  }
20602
20627
  }
20603
20628
 
20604
- function validateObject(options) {
20605
- const key = options.key;
20606
- const object = options.value;
20607
- const elementSpecs = options.valueSpec || {};
20608
- const elementValidators = options.objectElementValidators || {};
20609
- const style = options.style;
20610
- const styleSpec = options.styleSpec;
20611
- let errors = [];
20612
- const type = getType(object);
20613
- if (type !== 'object') {
20614
- return [new ValidationError(key, object, `object expected, ${ type } found`)];
20615
- }
20616
- for (const objectKey in object) {
20617
- const elementSpecKey = objectKey.split('.')[0];
20618
- const elementSpec = elementSpecs[elementSpecKey] || elementSpecs['*'];
20619
- let validateElement;
20620
- if (elementValidators[elementSpecKey]) {
20621
- validateElement = elementValidators[elementSpecKey];
20622
- } else if (elementSpecs[elementSpecKey]) {
20623
- validateElement = validate;
20624
- } else if (elementValidators['*']) {
20625
- validateElement = elementValidators['*'];
20626
- } else if (elementSpecs['*']) {
20627
- validateElement = validate;
20628
- }
20629
- if (!validateElement) {
20630
- errors.push(new ValidationWarning(key, object[objectKey], `unknown property "${ objectKey }"`));
20631
- continue;
20632
- }
20633
- errors = errors.concat(validateElement({
20634
- key: (key ? `${ key }.` : key) + objectKey,
20635
- value: object[objectKey],
20636
- valueSpec: elementSpec,
20637
- style,
20638
- styleSpec,
20639
- object,
20640
- objectKey
20641
- }, object));
20642
- }
20643
- for (const elementSpecKey in elementSpecs) {
20644
- if (elementValidators[elementSpecKey]) {
20645
- continue;
20646
- }
20647
- if (elementSpecs[elementSpecKey].required && elementSpecs[elementSpecKey]['default'] === void 0 && object[elementSpecKey] === void 0) {
20648
- errors.push(new ValidationError(key, object, `missing required property "${ elementSpecKey }"`));
20649
- }
20650
- }
20651
- return errors;
20652
- }
20653
-
20654
20629
  function validateImport(options) {
20630
+ const key = options.key;
20655
20631
  const {value, styleSpec} = options;
20632
+ if (!isObject(value)) {
20633
+ return [new ValidationError(key, value, `import must be an object`)];
20634
+ }
20656
20635
  const {data, ...importSpec} = value;
20657
20636
  Object.defineProperty(importSpec, '__line__', {
20658
20637
  value: value.__line__,
@@ -20663,12 +20642,12 @@ ${ JSON.stringify(filterExp, null, 2) }
20663
20642
  valueSpec: styleSpec.import
20664
20643
  }));
20665
20644
  if (unbundle(importSpec.id) === '') {
20666
- const key = `${ options.key }.id`;
20667
- errors.push(new ValidationError(key, importSpec, `import id can't be an empty string`));
20645
+ const key2 = `${ options.key }.id`;
20646
+ errors.push(new ValidationError(key2, importSpec, `import id can't be an empty string`));
20668
20647
  }
20669
20648
  if (data) {
20670
- const key = `${ options.key }.data`;
20671
- errors = errors.concat(validateStyle$2(data, styleSpec, { key }));
20649
+ const key2 = `${ options.key }.data`;
20650
+ errors = errors.concat(validateStyle$2(data, styleSpec, { key: key2 }));
20672
20651
  }
20673
20652
  return errors;
20674
20653
  }
@@ -20680,7 +20659,7 @@ ${ JSON.stringify(filterExp, null, 2) }
20680
20659
  const styleSpec = options.styleSpec;
20681
20660
  const key = options.key;
20682
20661
  const validateArrayElement = options.arrayElementValidator || validate;
20683
- if (getType(array) !== 'array') {
20662
+ if (!Array.isArray(array)) {
20684
20663
  return [new ValidationError(key, array, `array expected, ${ getType(array) } found`)];
20685
20664
  }
20686
20665
  if (arraySpec.length && array.length !== arraySpec.length) {
@@ -20690,16 +20669,16 @@ ${ JSON.stringify(filterExp, null, 2) }
20690
20669
  return [new ValidationError(key, array, `array length at least ${ arraySpec['min-length'] } expected, length ${ array.length } found`)];
20691
20670
  }
20692
20671
  let arrayElementSpec = {
20693
- 'type': arraySpec.value,
20694
- 'values': arraySpec.values,
20695
- 'minimum': arraySpec.minimum,
20696
- 'maximum': arraySpec.maximum,
20672
+ type: arraySpec.value,
20673
+ values: arraySpec.values,
20674
+ minimum: arraySpec.minimum,
20675
+ maximum: arraySpec.maximum,
20697
20676
  function: void 0
20698
20677
  };
20699
20678
  if (styleSpec.$version < 7) {
20700
20679
  arrayElementSpec.function = arraySpec.function;
20701
20680
  }
20702
- if (getType(arraySpec.value) === 'object') {
20681
+ if (isObject(arraySpec.value)) {
20703
20682
  arrayElementSpec = arraySpec.value;
20704
20683
  }
20705
20684
  let errors = [];
@@ -20721,16 +20700,15 @@ ${ JSON.stringify(filterExp, null, 2) }
20721
20700
  const key = options.key;
20722
20701
  const value = options.value;
20723
20702
  const valueSpec = options.valueSpec;
20724
- let type = getType(value);
20725
- if (type === 'number' && value !== value) {
20726
- type = 'NaN';
20703
+ if (!isNumber(value)) {
20704
+ return [new ValidationError(key, value, `number expected, ${ getType(value) } found`)];
20727
20705
  }
20728
- if (type !== 'number') {
20729
- return [new ValidationError(key, value, `number expected, ${ type } found`)];
20706
+ if (value !== value) {
20707
+ return [new ValidationError(key, value, `number expected, NaN found`)];
20730
20708
  }
20731
20709
  if ('minimum' in valueSpec) {
20732
20710
  let specMin = valueSpec.minimum;
20733
- if (getType(valueSpec.minimum) === 'array') {
20711
+ if (Array.isArray(valueSpec.minimum)) {
20734
20712
  const i = options.arrayIndex;
20735
20713
  specMin = valueSpec.minimum[i];
20736
20714
  }
@@ -20740,7 +20718,7 @@ ${ JSON.stringify(filterExp, null, 2) }
20740
20718
  }
20741
20719
  if ('maximum' in valueSpec) {
20742
20720
  let specMax = valueSpec.maximum;
20743
- if (getType(valueSpec.maximum) === 'array') {
20721
+ if (Array.isArray(valueSpec.maximum)) {
20744
20722
  const i = options.arrayIndex;
20745
20723
  specMax = valueSpec.maximum[i];
20746
20724
  }
@@ -20751,16 +20729,25 @@ ${ JSON.stringify(filterExp, null, 2) }
20751
20729
  return [];
20752
20730
  }
20753
20731
 
20732
+ function hasObjectStops(value) {
20733
+ const stops = value['stops'];
20734
+ return Array.isArray(stops) && Array.isArray(stops[0]) && isObject(stops[0][0]);
20735
+ }
20754
20736
  function validateFunction(options) {
20737
+ const key = options.key;
20738
+ const value = options.value;
20739
+ if (!isObject(value)) {
20740
+ return [new ValidationError(key, value, `object expected, ${ getType(value) } found`)];
20741
+ }
20755
20742
  const functionValueSpec = options.valueSpec;
20756
- const functionType = unbundle(options.value.type);
20743
+ const functionType = unbundle(value.type);
20757
20744
  let stopKeyType;
20758
20745
  let stopDomainValues = {};
20759
20746
  let previousStopDomainValue;
20760
20747
  let previousStopDomainZoom;
20761
- const isZoomFunction = functionType !== 'categorical' && options.value.property === void 0;
20748
+ const isZoomFunction = functionType !== 'categorical' && value.property === void 0;
20762
20749
  const isPropertyFunction = !isZoomFunction;
20763
- const isZoomAndPropertyFunction = getType(options.value.stops) === 'array' && getType(options.value.stops[0]) === 'array' && getType(options.value.stops[0][0]) === 'object';
20750
+ const isZoomAndPropertyFunction = hasObjectStops(value);
20764
20751
  const errors = validateObject({
20765
20752
  key: options.key,
20766
20753
  value: options.value,
@@ -20775,20 +20762,20 @@ ${ JSON.stringify(filterExp, null, 2) }
20775
20762
  if (functionType === 'identity' && isZoomFunction) {
20776
20763
  errors.push(new ValidationError(options.key, options.value, 'missing required property "property"'));
20777
20764
  }
20778
- if (functionType !== 'identity' && !options.value.stops) {
20765
+ if (functionType !== 'identity' && !value.stops) {
20779
20766
  errors.push(new ValidationError(options.key, options.value, 'missing required property "stops"'));
20780
20767
  }
20781
- if (functionType === 'exponential' && options.valueSpec.expression && !supportsInterpolation(options.valueSpec)) {
20768
+ if (functionType === 'exponential' && functionValueSpec.expression && !supportsInterpolation(functionValueSpec)) {
20782
20769
  errors.push(new ValidationError(options.key, options.value, 'exponential functions not supported'));
20783
20770
  }
20784
20771
  if (options.styleSpec.$version >= 8) {
20785
- if (isPropertyFunction && !supportsPropertyExpression(options.valueSpec)) {
20772
+ if (isPropertyFunction && !supportsPropertyExpression(functionValueSpec)) {
20786
20773
  errors.push(new ValidationError(options.key, options.value, 'property functions not supported'));
20787
- } else if (isZoomFunction && !supportsZoomExpression(options.valueSpec)) {
20774
+ } else if (isZoomFunction && !supportsZoomExpression(functionValueSpec)) {
20788
20775
  errors.push(new ValidationError(options.key, options.value, 'zoom functions not supported'));
20789
20776
  }
20790
20777
  }
20791
- if ((functionType === 'categorical' || isZoomAndPropertyFunction) && options.value.property === void 0) {
20778
+ if ((functionType === 'categorical' || isZoomAndPropertyFunction) && value.property === void 0) {
20792
20779
  errors.push(new ValidationError(options.key, options.value, '"property" property is required'));
20793
20780
  }
20794
20781
  return errors;
@@ -20797,46 +20784,47 @@ ${ JSON.stringify(filterExp, null, 2) }
20797
20784
  return [new ValidationError(options2.key, options2.value, 'identity function may not have a "stops" property')];
20798
20785
  }
20799
20786
  let errors2 = [];
20800
- const value = options2.value;
20787
+ const value2 = options2.value;
20801
20788
  errors2 = errors2.concat(validateArray({
20802
20789
  key: options2.key,
20803
- value,
20790
+ value: value2,
20804
20791
  valueSpec: options2.valueSpec,
20805
20792
  style: options2.style,
20806
20793
  styleSpec: options2.styleSpec,
20807
20794
  arrayElementValidator: validateFunctionStop
20808
20795
  }));
20809
- if (getType(value) === 'array' && value.length === 0) {
20810
- errors2.push(new ValidationError(options2.key, value, 'array must have at least one stop'));
20796
+ if (Array.isArray(value2) && value2.length === 0) {
20797
+ errors2.push(new ValidationError(options2.key, value2, 'array must have at least one stop'));
20811
20798
  }
20812
20799
  return errors2;
20813
20800
  }
20814
20801
  function validateFunctionStop(options2) {
20815
20802
  let errors2 = [];
20816
- const value = options2.value;
20817
- const key = options2.key;
20818
- if (getType(value) !== 'array') {
20819
- return [new ValidationError(key, value, `array expected, ${ getType(value) } found`)];
20803
+ const value2 = options2.value;
20804
+ const key2 = options2.key;
20805
+ if (!Array.isArray(value2)) {
20806
+ return [new ValidationError(key2, value2, `array expected, ${ getType(value2) } found`)];
20820
20807
  }
20821
- if (value.length !== 2) {
20822
- return [new ValidationError(key, value, `array length 2 expected, length ${ value.length } found`)];
20808
+ if (value2.length !== 2) {
20809
+ return [new ValidationError(key2, value2, `array length 2 expected, length ${ value2.length } found`)];
20823
20810
  }
20824
20811
  if (isZoomAndPropertyFunction) {
20825
- if (getType(value[0]) !== 'object') {
20826
- return [new ValidationError(key, value, `object expected, ${ getType(value[0]) } found`)];
20812
+ if (!isObject(value2[0])) {
20813
+ return [new ValidationError(key2, value2, `object expected, ${ getType(value2[0]) } found`)];
20827
20814
  }
20828
- if (value[0].zoom === void 0) {
20829
- return [new ValidationError(key, value, 'object stop key must have zoom')];
20815
+ const stopKey = value2[0];
20816
+ if (stopKey.zoom === void 0) {
20817
+ return [new ValidationError(key2, value2, 'object stop key must have zoom')];
20830
20818
  }
20831
- if (value[0].value === void 0) {
20832
- return [new ValidationError(key, value, 'object stop key must have value')];
20819
+ if (stopKey.value === void 0) {
20820
+ return [new ValidationError(key2, value2, 'object stop key must have value')];
20833
20821
  }
20834
- const nextStopDomainZoom = unbundle(value[0].zoom);
20822
+ const nextStopDomainZoom = unbundle(stopKey.zoom);
20835
20823
  if (typeof nextStopDomainZoom !== 'number') {
20836
- return [new ValidationError(key, value[0].zoom, 'stop zoom values must be numbers')];
20824
+ return [new ValidationError(key2, stopKey.zoom, 'stop zoom values must be numbers')];
20837
20825
  }
20838
20826
  if (previousStopDomainZoom && previousStopDomainZoom > nextStopDomainZoom) {
20839
- return [new ValidationError(key, value[0].zoom, 'stop zoom values must appear in ascending order')];
20827
+ return [new ValidationError(key2, stopKey.zoom, 'stop zoom values must appear in ascending order')];
20840
20828
  }
20841
20829
  if (nextStopDomainZoom !== previousStopDomainZoom) {
20842
20830
  previousStopDomainZoom = nextStopDomainZoom;
@@ -20844,8 +20832,8 @@ ${ JSON.stringify(filterExp, null, 2) }
20844
20832
  stopDomainValues = {};
20845
20833
  }
20846
20834
  errors2 = errors2.concat(validateObject({
20847
- key: `${ key }[0]`,
20848
- value: value[0],
20835
+ key: `${ key2 }[0]`,
20836
+ value: value2[0],
20849
20837
  valueSpec: { zoom: {} },
20850
20838
  style: options2.style,
20851
20839
  styleSpec: options2.styleSpec,
@@ -20856,18 +20844,18 @@ ${ JSON.stringify(filterExp, null, 2) }
20856
20844
  }));
20857
20845
  } else {
20858
20846
  errors2 = errors2.concat(validateStopDomainValue({
20859
- key: `${ key }[0]`,
20860
- value: value[0],
20847
+ key: `${ key2 }[0]`,
20848
+ value: value2[0],
20861
20849
  style: options2.style,
20862
20850
  styleSpec: options2.styleSpec
20863
- }, value));
20851
+ }, value2));
20864
20852
  }
20865
- if (isExpression(deepUnbundle(value[1]))) {
20866
- return errors2.concat([new ValidationError(`${ key }[1]`, value[1], 'expressions are not allowed in function stops.')]);
20853
+ if (isExpression(deepUnbundle(value2[1]))) {
20854
+ return errors2.concat([new ValidationError(`${ key2 }[1]`, value2[1], 'expressions are not allowed in function stops.')]);
20867
20855
  }
20868
20856
  return errors2.concat(validate({
20869
- key: `${ key }[1]`,
20870
- value: value[1],
20857
+ key: `${ key2 }[1]`,
20858
+ value: value2[1],
20871
20859
  valueSpec: functionValueSpec,
20872
20860
  style: options2.style,
20873
20861
  styleSpec: options2.styleSpec
@@ -20875,14 +20863,14 @@ ${ JSON.stringify(filterExp, null, 2) }
20875
20863
  }
20876
20864
  function validateStopDomainValue(options2, stop) {
20877
20865
  const type = getType(options2.value);
20878
- const value = unbundle(options2.value);
20866
+ const value2 = unbundle(options2.value);
20879
20867
  const reportValue = options2.value !== null ? options2.value : stop;
20880
20868
  if (!stopKeyType) {
20881
20869
  stopKeyType = type;
20882
20870
  } else if (type !== stopKeyType) {
20883
20871
  return [new ValidationError(options2.key, reportValue, `${ type } stop domain type must match previous stop domain type ${ stopKeyType }`)];
20884
20872
  }
20885
- if (type !== 'number' && type !== 'string' && type !== 'boolean' && typeof value !== 'number' && typeof value !== 'string' && typeof value !== 'boolean') {
20873
+ if (type !== 'number' && type !== 'string' && type !== 'boolean' && typeof value2 !== 'number' && typeof value2 !== 'string' && typeof value2 !== 'boolean') {
20886
20874
  return [new ValidationError(options2.key, reportValue, 'stop domain value must be a number, string, or boolean')];
20887
20875
  }
20888
20876
  if (type !== 'number' && functionType !== 'categorical') {
@@ -20892,18 +20880,18 @@ ${ JSON.stringify(filterExp, null, 2) }
20892
20880
  }
20893
20881
  return [new ValidationError(options2.key, reportValue, message)];
20894
20882
  }
20895
- if (functionType === 'categorical' && type === 'number' && (typeof value !== 'number' || !isFinite(value) || Math.floor(value) !== value)) {
20896
- return [new ValidationError(options2.key, reportValue, `integer expected, found ${ String(value) }`)];
20883
+ if (functionType === 'categorical' && type === 'number' && (typeof value2 !== 'number' || !isFinite(value2) || Math.floor(value2) !== value2)) {
20884
+ return [new ValidationError(options2.key, reportValue, `integer expected, found ${ String(value2) }`)];
20897
20885
  }
20898
- if (functionType !== 'categorical' && type === 'number' && typeof value === 'number' && typeof previousStopDomainValue === 'number' && previousStopDomainValue !== void 0 && value < previousStopDomainValue) {
20886
+ if (functionType !== 'categorical' && type === 'number' && typeof value2 === 'number' && typeof previousStopDomainValue === 'number' && previousStopDomainValue !== void 0 && value2 < previousStopDomainValue) {
20899
20887
  return [new ValidationError(options2.key, reportValue, 'stop domain values must appear in ascending order')];
20900
20888
  } else {
20901
- previousStopDomainValue = value;
20889
+ previousStopDomainValue = value2;
20902
20890
  }
20903
- if (functionType === 'categorical' && value in stopDomainValues) {
20891
+ if (functionType === 'categorical' && value2 in stopDomainValues) {
20904
20892
  return [new ValidationError(options2.key, reportValue, 'stop domain values must be unique')];
20905
20893
  } else {
20906
- stopDomainValues[value] = true;
20894
+ stopDomainValues[value2] = true;
20907
20895
  }
20908
20896
  return [];
20909
20897
  }
@@ -20979,19 +20967,15 @@ ${ JSON.stringify(filterExp, null, 2) }
20979
20967
  function validateBoolean(options) {
20980
20968
  const value = options.value;
20981
20969
  const key = options.key;
20982
- const type = getType(value);
20983
- if (type !== 'boolean') {
20984
- return [new ValidationError(key, value, `boolean expected, ${ type } found`)];
20970
+ if (!isBoolean(value)) {
20971
+ return [new ValidationError(key, value, `boolean expected, ${ getType(value) } found`)];
20985
20972
  }
20986
20973
  return [];
20987
20974
  }
20988
20975
 
20989
- function validateColor(options) {
20990
- const key = options.key;
20991
- const value = options.value;
20992
- const type = getType(value);
20993
- if (type !== 'string') {
20994
- return [new ValidationError(key, value, `color expected, ${ type } found`)];
20976
+ function validateColor({key, value}) {
20977
+ if (!isString(value)) {
20978
+ return [new ValidationError(key, value, `color expected, ${ getType(value) } found`)];
20995
20979
  }
20996
20980
  if (csscolorparserExports.parseCSSColor(value) === null) {
20997
20981
  return [new ValidationError(key, value, `color expected, "${ value }" found`)];
@@ -21030,22 +21014,18 @@ ${ JSON.stringify(filterExp, null, 2) }
21030
21014
  function validateNonExpressionFilter(options) {
21031
21015
  const value = options.value;
21032
21016
  const key = options.key;
21033
- if (getType(value) !== 'array') {
21017
+ if (!Array.isArray(value)) {
21034
21018
  return [new ValidationError(key, value, `array expected, ${ getType(value) } found`)];
21035
21019
  }
21036
- const styleSpec = options.styleSpec;
21037
- let type;
21038
- let errors = [];
21039
21020
  if (value.length < 1) {
21040
21021
  return [new ValidationError(key, value, 'filter array must have at least 1 element')];
21041
21022
  }
21042
- errors = errors.concat(validateEnum({
21023
+ const styleSpec = options.styleSpec;
21024
+ let errors = validateEnum({
21043
21025
  key: `${ key }[0]`,
21044
21026
  value: value[0],
21045
- valueSpec: styleSpec.filter_operator,
21046
- style: options.style,
21047
- styleSpec: options.styleSpec
21048
- }));
21027
+ valueSpec: styleSpec.filter_operator
21028
+ });
21049
21029
  switch (unbundle(value[0])) {
21050
21030
  case '<':
21051
21031
  case '<=':
@@ -21066,23 +21046,19 @@ ${ JSON.stringify(filterExp, null, 2) }
21066
21046
  case 'in':
21067
21047
  case '!in':
21068
21048
  if (value.length >= 2) {
21069
- type = getType(value[1]);
21070
- if (type !== 'string') {
21071
- errors.push(new ValidationError(`${ key }[1]`, value[1], `string expected, ${ type } found`));
21049
+ if (!isString(value[1])) {
21050
+ errors.push(new ValidationError(`${ key }[1]`, value[1], `string expected, ${ getType(value[1]) } found`));
21072
21051
  }
21073
21052
  }
21074
21053
  for (let i = 2; i < value.length; i++) {
21075
- type = getType(value[i]);
21076
21054
  if (unbundle(value[1]) === '$type') {
21077
21055
  errors = errors.concat(validateEnum({
21078
21056
  key: `${ key }[${ i }]`,
21079
21057
  value: value[i],
21080
- valueSpec: styleSpec.geometry_type,
21081
- style: options.style,
21082
- styleSpec: options.styleSpec
21058
+ valueSpec: styleSpec.geometry_type
21083
21059
  }));
21084
- } else if (type !== 'string' && type !== 'number' && type !== 'boolean') {
21085
- errors.push(new ValidationError(`${ key }[${ i }]`, value[i], `string, number, or boolean expected, ${ type } found`));
21060
+ } else if (!isString(value[i]) && !isNumber(value[i]) && !isBoolean(value[i])) {
21061
+ errors.push(new ValidationError(`${ key }[${ i }]`, value[i], `string, number, or boolean expected, ${ getType(value[i]) } found.`));
21086
21062
  }
21087
21063
  }
21088
21064
  break;
@@ -21100,11 +21076,10 @@ ${ JSON.stringify(filterExp, null, 2) }
21100
21076
  break;
21101
21077
  case 'has':
21102
21078
  case '!has':
21103
- type = getType(value[1]);
21104
21079
  if (value.length !== 2) {
21105
21080
  errors.push(new ValidationError(key, value, `filter array for "${ value[0] }" operator must have 2 elements`));
21106
- } else if (type !== 'string') {
21107
- errors.push(new ValidationError(`${ key }[1]`, value[1], `string expected, ${ type } found`));
21081
+ } else if (!isString(value[1])) {
21082
+ errors.push(new ValidationError(`${ key }[1]`, value[1], `string expected, ${ getType(value[1]) } found`));
21108
21083
  }
21109
21084
  break;
21110
21085
  }
@@ -21122,7 +21097,7 @@ ${ JSON.stringify(filterExp, null, 2) }
21122
21097
  if (!layerSpec)
21123
21098
  return [];
21124
21099
  const useThemeMatch = propertyKey.match(/^(.*)-use-theme$/);
21125
- if (propertyType === 'paint' && useThemeMatch && layerSpec[useThemeMatch[1]]) {
21100
+ if (useThemeMatch && layerSpec[useThemeMatch[1]]) {
21126
21101
  if (isExpression(value)) {
21127
21102
  const errors2 = [];
21128
21103
  return errors2.concat(validate({
@@ -21169,7 +21144,7 @@ ${ JSON.stringify(filterExp, null, 2) }
21169
21144
  return [new ValidationWarning(key, value, `unknown property "${ propertyKey }"`)];
21170
21145
  }
21171
21146
  let tokenMatch;
21172
- if (getType(value) === 'string' && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) {
21147
+ if (isString(value) && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) {
21173
21148
  const example = `\`{ "type": "identity", "property": ${ tokenMatch ? JSON.stringify(tokenMatch[1]) : '"_"' } }\``;
21174
21149
  return [new ValidationError(key, value, `"${ propertyKey }" does not support interpolation syntax
21175
21150
  Use an identity property function instead: ${ example }.`)];
@@ -21219,6 +21194,9 @@ Use an identity property function instead: ${ example }.`)];
21219
21194
  const key = options.key;
21220
21195
  const style = options.style;
21221
21196
  const styleSpec = options.styleSpec;
21197
+ if (!isObject(layer)) {
21198
+ return [new ValidationError(key, layer, `object expected`)];
21199
+ }
21222
21200
  if (!layer.type && !layer.ref) {
21223
21201
  errors.push(new ValidationError(key, layer, 'either "type" or "ref" is required'));
21224
21202
  }
@@ -21229,7 +21207,7 @@ Use an identity property function instead: ${ example }.`)];
21229
21207
  for (let i = 0; i < options.arrayIndex; i++) {
21230
21208
  const otherLayer = style.layers[i];
21231
21209
  if (unbundle(otherLayer.id) === layerId) {
21232
- errors.push(new ValidationError(key, layer.id, `duplicate layer id "${ layer.id }", previously used at line ${ otherLayer.id.__line__ }`));
21210
+ errors.push(new ValidationError(key, layer.id, `duplicate layer id "${ layerId }", previously used at line ${ otherLayer.id.__line__ }`));
21233
21211
  }
21234
21212
  }
21235
21213
  }
@@ -21261,6 +21239,8 @@ Use an identity property function instead: ${ example }.`)];
21261
21239
  } else if (!(type === 'background' || type === 'sky' || type === 'slot')) {
21262
21240
  if (!layer.source) {
21263
21241
  errors.push(new ValidationError(key, layer, 'missing required property "source"'));
21242
+ } else if (!isString(layer.source)) {
21243
+ errors.push(new ValidationError(`${ key }.source`, layer.source, '"source" must be a string'));
21264
21244
  } else {
21265
21245
  const source = style.sources && style.sources[layer.source];
21266
21246
  const sourceType = source && unbundle(source.type);
@@ -21350,14 +21330,11 @@ Use an identity property function instead: ${ example }.`)];
21350
21330
  return errors;
21351
21331
  }
21352
21332
 
21353
- function validateString(options) {
21354
- const value = options.value;
21355
- const key = options.key;
21356
- const type = getType(value);
21357
- if (type !== 'string') {
21358
- return [new ValidationError(key, value, `string expected, ${ type } found`)];
21333
+ function validateString({key, value}) {
21334
+ if (isString(value)) {
21335
+ return [];
21359
21336
  }
21360
- return [];
21337
+ return [new ValidationError(key, value, `string expected, ${ getType(value) } found`)];
21361
21338
  }
21362
21339
 
21363
21340
  const objectElementValidators = { promoteId: validatePromoteId };
@@ -21366,7 +21343,10 @@ Use an identity property function instead: ${ example }.`)];
21366
21343
  const key = options.key;
21367
21344
  const styleSpec = options.styleSpec;
21368
21345
  const style = options.style;
21369
- if (!value.type) {
21346
+ if (!isObject(value)) {
21347
+ return [new ValidationError(key, value, `object expected, ${ getType(value) } found`)];
21348
+ }
21349
+ if (!('type' in value)) {
21370
21350
  return [new ValidationError(key, value, '"type" is required')];
21371
21351
  }
21372
21352
  const type = unbundle(value.type);
@@ -21377,7 +21357,7 @@ Use an identity property function instead: ${ example }.`)];
21377
21357
  'raster-dem',
21378
21358
  'raster-array'
21379
21359
  ].includes(type)) {
21380
- if (!value.url && !value.tiles) {
21360
+ if (!('url' in value) && !('tiles' in value)) {
21381
21361
  errors.push(new ValidationWarning(key, value, 'Either "url" or "tiles" is required.'));
21382
21362
  }
21383
21363
  }
@@ -21404,9 +21384,16 @@ Use an identity property function instead: ${ example }.`)];
21404
21384
  styleSpec,
21405
21385
  objectElementValidators
21406
21386
  });
21407
- if (value.cluster) {
21387
+ if ('cluster' in value && 'clusterProperties' in value) {
21388
+ if (!isObject(value.clusterProperties)) {
21389
+ return [new ValidationError(`${ key }.clusterProperties`, value, `object expected, ${ getType(value) } found`)];
21390
+ }
21408
21391
  for (const prop in value.clusterProperties) {
21409
- const [operator, mapExpr] = value.clusterProperties[prop];
21392
+ const propValue = value.clusterProperties[prop];
21393
+ if (!Array.isArray(propValue)) {
21394
+ return [new ValidationError(`${ key }.clusterProperties.${ prop }`, propValue, 'array expected')];
21395
+ }
21396
+ const [operator, mapExpr] = propValue;
21410
21397
  const reduceExpr = typeof operator === 'string' ? [
21411
21398
  operator,
21412
21399
  ['accumulated'],
@@ -21463,18 +21450,19 @@ Use an identity property function instead: ${ example }.`)];
21463
21450
  }, []);
21464
21451
  }
21465
21452
  function validatePromoteId({key, value}) {
21466
- if (getType(value) === 'string') {
21453
+ if (isString(value)) {
21467
21454
  return validateString({
21468
21455
  key,
21469
21456
  value
21470
21457
  });
21471
- } else if (Array.isArray(value)) {
21472
- const errors = [];
21458
+ }
21459
+ if (Array.isArray(value)) {
21460
+ const errors2 = [];
21473
21461
  const unbundledValue = deepUnbundle(value);
21474
21462
  const expression = createExpression(unbundledValue);
21475
21463
  if (expression.result === 'error') {
21476
21464
  expression.value.forEach(err => {
21477
- errors.push(new ValidationError(`${ key }${ err.key }`, null, `${ err.message }`));
21465
+ errors2.push(new ValidationError(`${ key }${ err.key }`, null, `${ err.message }`));
21478
21466
  });
21479
21467
  }
21480
21468
  const parsed = expression.value.expression;
@@ -21492,19 +21480,21 @@ Use an identity property function instead: ${ example }.`)];
21492
21480
  'raster-particle-speed'
21493
21481
  ]);
21494
21482
  if (!onlyFeatureDependent) {
21495
- errors.push(new ValidationError(`${ key }`, null, 'promoteId expression should be only feature dependent'));
21483
+ errors2.push(new ValidationError(`${ key }`, null, 'promoteId expression should be only feature dependent'));
21496
21484
  }
21497
- return errors;
21498
- } else {
21499
- const errors = [];
21500
- for (const prop in value) {
21501
- errors.push(...validatePromoteId({
21502
- key: `${ key }.${ prop }`,
21503
- value: value[prop]
21504
- }));
21505
- }
21506
- return errors;
21485
+ return errors2;
21507
21486
  }
21487
+ if (!isObject(value)) {
21488
+ return [new ValidationError(key, value, `string, expression or object expected, "${ getType(value) }" found`)];
21489
+ }
21490
+ const errors = [];
21491
+ for (const prop in value) {
21492
+ errors.push(...validatePromoteId({
21493
+ key: `${ key }.${ prop }`,
21494
+ value: value[prop]
21495
+ }));
21496
+ }
21497
+ return errors;
21508
21498
  }
21509
21499
 
21510
21500
  function isValidUrl(str, allowRelativeUrls) {
@@ -21518,19 +21508,16 @@ Use an identity property function instead: ${ example }.`)];
21518
21508
  }
21519
21509
  function validateModel(options) {
21520
21510
  const url = options.value;
21521
- let errors = [];
21522
21511
  if (!url) {
21523
- return errors;
21512
+ return [];
21524
21513
  }
21525
- const type = getType(url);
21526
- if (type !== 'string') {
21527
- errors = errors.concat([new ValidationError(options.key, url, `string expected, "${ type }" found`)]);
21528
- return errors;
21514
+ if (!isString(url)) {
21515
+ return [new ValidationError(options.key, url, `string expected, "${ getType(url) }" found`)];
21529
21516
  }
21530
21517
  if (!isValidUrl(url, true)) {
21531
- errors = errors.concat([new ValidationError(options.key, url, `invalid url "${ url }"`)]);
21518
+ return [new ValidationError(options.key, url, `invalid url "${ url }"`)];
21532
21519
  }
21533
- return errors;
21520
+ return [];
21534
21521
  }
21535
21522
 
21536
21523
  function validateLight(options) {
@@ -21538,14 +21525,13 @@ Use an identity property function instead: ${ example }.`)];
21538
21525
  const styleSpec = options.styleSpec;
21539
21526
  const lightSpec = styleSpec.light;
21540
21527
  const style = options.style;
21541
- let errors = [];
21542
- const rootType = getType(light);
21543
21528
  if (light === void 0) {
21544
- return errors;
21545
- } else if (rootType !== 'object') {
21546
- errors = errors.concat([new ValidationError('light', light, `object expected, ${ rootType } found`)]);
21547
- return errors;
21529
+ return [];
21530
+ }
21531
+ if (!isObject(light)) {
21532
+ return [new ValidationError('light', light, `object expected, ${ getType(light) } found`)];
21548
21533
  }
21534
+ let errors = [];
21549
21535
  for (const key in light) {
21550
21536
  const transitionMatch = key.match(/^(.*)-transition$/);
21551
21537
  const useThemeMatch = key.match(/^(.*)-use-theme$/);
@@ -21582,30 +21568,32 @@ Use an identity property function instead: ${ example }.`)];
21582
21568
 
21583
21569
  function validateLights(options) {
21584
21570
  const light = options.value;
21585
- let errors = [];
21586
21571
  if (!light) {
21587
- return errors;
21572
+ return [];
21588
21573
  }
21589
- const type = getType(light);
21590
- if (type !== 'object') {
21591
- errors = errors.concat([new ValidationError('light-3d', light, `object expected, ${ type } found`)]);
21592
- return errors;
21574
+ const key = options.key;
21575
+ if (!isObject(light)) {
21576
+ return [new ValidationError(key, light, `object expected, ${ getType(light) } found`)];
21593
21577
  }
21578
+ let errors = [];
21594
21579
  const styleSpec = options.styleSpec;
21595
21580
  const lightSpec = styleSpec['light-3d'];
21596
- const key = options.key;
21597
21581
  const style = options.style;
21598
21582
  const lights = options.style.lights;
21599
- for (const key2 of [
21583
+ for (const prop of [
21600
21584
  'type',
21601
21585
  'id'
21602
21586
  ]) {
21603
- if (!(key2 in light)) {
21604
- errors = errors.concat([new ValidationError('light-3d', light, `missing property ${ key2 } on light`)]);
21587
+ if (!(prop in light)) {
21588
+ errors = errors.concat([new ValidationError(key, light, `missing property "${ prop }"`)]);
21605
21589
  return errors;
21606
21590
  }
21607
21591
  }
21608
- if (light.type && lights) {
21592
+ if (!isString(light.type)) {
21593
+ errors = errors.concat([new ValidationError(`${ key }.type`, light.type, `string expected`)]);
21594
+ return errors;
21595
+ }
21596
+ if (lights) {
21609
21597
  for (let i = 0; i < options.arrayIndex; i++) {
21610
21598
  const lightType2 = unbundle(light.type);
21611
21599
  const otherLight = lights[i];
@@ -21614,18 +21602,17 @@ Use an identity property function instead: ${ example }.`)];
21614
21602
  }
21615
21603
  }
21616
21604
  }
21617
- const lightType = `properties_light_${ light['type'] }`;
21605
+ const lightType = `properties_light_${ light.type }`;
21618
21606
  if (!(lightType in styleSpec)) {
21619
- errors = errors.concat([new ValidationError('light-3d', light, `Invalid light type ${ light['type'] }`)]);
21607
+ errors = errors.concat([new ValidationError(`${ key }.type`, light, `Invalid light type ${ light.type }`)]);
21620
21608
  return errors;
21621
21609
  }
21622
21610
  const lightPropertySpec = styleSpec[lightType];
21623
21611
  for (const key2 in light) {
21624
21612
  if (key2 === 'properties') {
21625
21613
  const properties = light[key2];
21626
- const propertiesType = getType(properties);
21627
- if (propertiesType !== 'object') {
21628
- errors = errors.concat([new ValidationError('properties', properties, `object expected, ${ propertiesType } found`)]);
21614
+ if (!isObject(properties)) {
21615
+ errors = errors.concat([new ValidationError('properties', properties, `object expected, ${ getType(properties) } found`)]);
21629
21616
  return errors;
21630
21617
  }
21631
21618
  for (const propertyKey in properties) {
@@ -21682,16 +21669,13 @@ Use an identity property function instead: ${ example }.`)];
21682
21669
  const style = options.style;
21683
21670
  const styleSpec = options.styleSpec;
21684
21671
  const terrainSpec = styleSpec.terrain;
21685
- let errors = [];
21686
- const rootType = getType(terrain);
21687
- if (terrain === void 0) {
21688
- return errors;
21689
- } else if (rootType === 'null') {
21690
- return errors;
21691
- } else if (rootType !== 'object') {
21692
- errors = errors.concat([new ValidationError('terrain', terrain, `object expected, ${ rootType } found`)]);
21693
- return errors;
21672
+ if (terrain == null) {
21673
+ return [];
21674
+ }
21675
+ if (!isObject(terrain)) {
21676
+ return [new ValidationError('terrain', terrain, `object expected, ${ getType(terrain) } found`)];
21694
21677
  }
21678
+ let errors = [];
21695
21679
  for (const key2 in terrain) {
21696
21680
  const transitionMatch = key2.match(/^(.*)-transition$/);
21697
21681
  const useThemeMatch = key2.match(/^(.*)-use-theme$/);
@@ -21725,13 +21709,15 @@ Use an identity property function instead: ${ example }.`)];
21725
21709
  }
21726
21710
  if (!terrain.source) {
21727
21711
  errors.push(new ValidationError(key, terrain, `terrain is missing required property "source"`));
21712
+ } else if (!isString(terrain.source)) {
21713
+ errors.push(new ValidationError(`${ key }.source`, terrain.source, `source must be a string`));
21728
21714
  } else {
21729
21715
  const source = style.sources && style.sources[terrain.source];
21730
21716
  const sourceType = source && unbundle(source.type);
21731
21717
  if (!source) {
21732
- errors.push(new ValidationError(key, terrain.source, `source "${ terrain.source }" not found`));
21718
+ errors.push(new ValidationError(`${ key }.source`, terrain.source, `source "${ terrain.source }" not found`));
21733
21719
  } else if (sourceType !== 'raster-dem') {
21734
- errors.push(new ValidationError(key, terrain.source, `terrain cannot be used with a source of type ${ String(sourceType) }, it only be used with a "raster-dem" source type`));
21720
+ errors.push(new ValidationError(`${ key }.source`, terrain.source, `terrain cannot be used with a source of type ${ sourceType }, it only be used with a "raster-dem" source type`));
21735
21721
  }
21736
21722
  }
21737
21723
  return errors;
@@ -21742,14 +21728,13 @@ Use an identity property function instead: ${ example }.`)];
21742
21728
  const style = options.style;
21743
21729
  const styleSpec = options.styleSpec;
21744
21730
  const fogSpec = styleSpec.fog;
21745
- let errors = [];
21746
- const rootType = getType(fog);
21747
21731
  if (fog === void 0) {
21748
- return errors;
21749
- } else if (rootType !== 'object') {
21750
- errors = errors.concat([new ValidationError('fog', fog, `object expected, ${ rootType } found`)]);
21751
- return errors;
21732
+ return [];
21733
+ }
21734
+ if (!isObject(fog)) {
21735
+ return [new ValidationError('fog', fog, `object expected, ${ getType(fog) } found`)];
21752
21736
  }
21737
+ let errors = [];
21753
21738
  for (const key in fog) {
21754
21739
  const transitionMatch = key.match(/^(.*)-transition$/);
21755
21740
  const useThemeMatch = key.match(/^(.*)-use-theme$/);
@@ -21785,14 +21770,16 @@ Use an identity property function instead: ${ example }.`)];
21785
21770
  }
21786
21771
 
21787
21772
  function validateFormatted(options) {
21788
- if (validateString(options).length === 0) {
21773
+ const errors = validateString(options);
21774
+ if (errors.length === 0) {
21789
21775
  return [];
21790
21776
  }
21791
21777
  return validateExpression(options);
21792
21778
  }
21793
21779
 
21794
21780
  function validateImage(options) {
21795
- if (validateString(options).length === 0) {
21781
+ const errors = validateString(options);
21782
+ if (errors.length === 0) {
21796
21783
  return [];
21797
21784
  }
21798
21785
  return validateExpression(options);
@@ -21803,9 +21790,8 @@ Use an identity property function instead: ${ example }.`)];
21803
21790
  const styleSpec = options.styleSpec;
21804
21791
  const projectionSpec = styleSpec.projection;
21805
21792
  const style = options.style;
21806
- let errors = [];
21807
- const rootType = getType(projection);
21808
- if (rootType === 'object') {
21793
+ if (isObject(projection)) {
21794
+ let errors = [];
21809
21795
  for (const key in projection) {
21810
21796
  errors = errors.concat(validate({
21811
21797
  key,
@@ -21815,17 +21801,25 @@ Use an identity property function instead: ${ example }.`)];
21815
21801
  styleSpec
21816
21802
  }));
21817
21803
  }
21818
- } else if (rootType !== 'string') {
21819
- errors = errors.concat([new ValidationError('projection', projection, `object or string expected, ${ rootType } found`)]);
21804
+ return errors;
21820
21805
  }
21821
- return errors;
21806
+ if (!isString(projection)) {
21807
+ return [new ValidationError('projection', projection, `object or string expected, ${ getType(projection) } found`)];
21808
+ }
21809
+ return [];
21822
21810
  }
21823
21811
 
21812
+ function isSourceIconset(type, iconset) {
21813
+ return !!(type === 'source' && iconset.source);
21814
+ }
21824
21815
  function validateIconset(options) {
21825
21816
  const iconset = options.value;
21826
21817
  const key = options.key;
21827
21818
  const styleSpec = options.styleSpec;
21828
21819
  const style = options.style;
21820
+ if (!isObject(iconset)) {
21821
+ return [new ValidationError(key, iconset, 'object expected')];
21822
+ }
21829
21823
  if (!iconset.type) {
21830
21824
  return [new ValidationError(key, iconset, '"type" is required')];
21831
21825
  }
@@ -21838,7 +21832,7 @@ Use an identity property function instead: ${ example }.`)];
21838
21832
  style,
21839
21833
  styleSpec
21840
21834
  }));
21841
- if (type === 'source' && iconset.source) {
21835
+ if (isSourceIconset(type, iconset)) {
21842
21836
  const source = style.sources && style.sources[iconset.source];
21843
21837
  const sourceType = source && unbundle(source.type);
21844
21838
  if (!source) {
@@ -21878,49 +21872,98 @@ Use an identity property function instead: ${ example }.`)];
21878
21872
  const value = options.value;
21879
21873
  const valueSpec = options.valueSpec;
21880
21874
  const styleSpec = options.styleSpec;
21881
- if (valueSpec.expression && isFunction(unbundle(value))) {
21882
- return validateFunction(options);
21883
- } else if (valueSpec.expression && isExpression(deepUnbundle(value))) {
21884
- return validateExpression(options);
21885
- } else if (valueSpec.type && VALIDATORS[valueSpec.type]) {
21886
- const valid = VALIDATORS[valueSpec.type](options);
21887
- if (arrayAsExpression === true && valid.length > 0 && getType(options.value) === 'array') {
21875
+ if (valueSpec.expression) {
21876
+ if (isFunction(unbundle(value)))
21877
+ return validateFunction(options);
21878
+ if (isExpression(deepUnbundle(value)))
21879
+ return validateExpression(options);
21880
+ }
21881
+ if (valueSpec.type && VALIDATORS[valueSpec.type]) {
21882
+ const errors2 = VALIDATORS[valueSpec.type](options);
21883
+ if (arrayAsExpression === true && errors2.length > 0 && Array.isArray(options.value)) {
21888
21884
  return validateExpression(options);
21889
- } else {
21890
- return valid;
21891
21885
  }
21892
- } else {
21893
- const valid = validateObject(extend({}, options, { valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec }));
21894
- return valid;
21886
+ return errors2;
21895
21887
  }
21888
+ const errors = validateObject(Object.assign({}, options, { valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec }));
21889
+ return errors;
21896
21890
  }
21897
21891
 
21898
- function validateGlyphsURL (options) {
21899
- const value = options.value;
21892
+ function validateObject(options) {
21900
21893
  const key = options.key;
21901
- const errors = validateString(options);
21894
+ const object = options.value;
21895
+ const elementSpecs = options.valueSpec || {};
21896
+ const elementValidators = options.objectElementValidators || {};
21897
+ const style = options.style;
21898
+ const styleSpec = options.styleSpec;
21899
+ if (!isObject(object)) {
21900
+ return [new ValidationError(key, object, `object expected, ${ getType(object) } found`)];
21901
+ }
21902
+ let errors = [];
21903
+ for (const objectKey in object) {
21904
+ const elementSpecKey = objectKey.split('.')[0];
21905
+ const elementSpec = elementSpecs[elementSpecKey] || elementSpecs['*'];
21906
+ let validateElement;
21907
+ if (elementValidators[elementSpecKey]) {
21908
+ validateElement = elementValidators[elementSpecKey];
21909
+ } else if (elementSpecs[elementSpecKey]) {
21910
+ validateElement = validate;
21911
+ } else if (elementValidators['*']) {
21912
+ validateElement = elementValidators['*'];
21913
+ } else if (elementSpecs['*']) {
21914
+ validateElement = validate;
21915
+ }
21916
+ if (!validateElement) {
21917
+ errors.push(new ValidationWarning(key, object[objectKey], `unknown property "${ objectKey }"`));
21918
+ continue;
21919
+ }
21920
+ errors = errors.concat(validateElement({
21921
+ key: (key ? `${ key }.` : key) + objectKey,
21922
+ value: object[objectKey],
21923
+ valueSpec: elementSpec,
21924
+ style,
21925
+ styleSpec,
21926
+ object,
21927
+ objectKey
21928
+ }, object));
21929
+ }
21930
+ for (const elementSpecKey in elementSpecs) {
21931
+ if (elementValidators[elementSpecKey]) {
21932
+ continue;
21933
+ }
21934
+ if (elementSpecs[elementSpecKey].required && elementSpecs[elementSpecKey]['default'] === void 0 && object[elementSpecKey] === void 0) {
21935
+ errors.push(new ValidationError(key, object, `missing required property "${ elementSpecKey }"`));
21936
+ }
21937
+ }
21938
+ return errors;
21939
+ }
21940
+
21941
+ function validateGlyphsUrl({key, value}) {
21942
+ const errors = validateString({
21943
+ key,
21944
+ value
21945
+ });
21902
21946
  if (errors.length)
21903
21947
  return errors;
21904
- if (value.indexOf('{fontstack}') === -1) {
21948
+ const str = value;
21949
+ if (str.indexOf('{fontstack}') === -1) {
21905
21950
  errors.push(new ValidationError(key, value, '"glyphs" url must include a "{fontstack}" token'));
21906
21951
  }
21907
- if (value.indexOf('{range}') === -1) {
21952
+ if (str.indexOf('{range}') === -1) {
21908
21953
  errors.push(new ValidationError(key, value, '"glyphs" url must include a "{range}" token'));
21909
21954
  }
21910
21955
  return errors;
21911
21956
  }
21912
21957
 
21913
21958
  function validateStyle$2(style, styleSpec = v8, options = {}) {
21914
- const errors = validate({
21959
+ const errors = validateObject({
21915
21960
  key: options.key || '',
21916
21961
  value: style,
21917
- valueSpec: styleSpec.$root,
21962
+ valueSpec: Object.assign(styleSpec.$root, // Skip validation of the root properties that are not defined in the style spec (e.g. 'owner').
21963
+ { '*': { type: '*' } }),
21918
21964
  styleSpec,
21919
21965
  style,
21920
- objectElementValidators: {
21921
- glyphs: validateGlyphsURL,
21922
- '*': () => []
21923
- }
21966
+ objectElementValidators: { glyphs: validateGlyphsUrl }
21924
21967
  });
21925
21968
  return errors;
21926
21969
  }
@@ -23027,7 +23070,7 @@ Use an identity property function instead: ${ example }.`)];
23027
23070
  const SUPPORTED_SPEC_VERSION = 8;
23028
23071
  const MAX_SOURCES_IN_STYLE = 15;
23029
23072
  function isValid(value, regex) {
23030
- if (!value || getType(value) !== 'string')
23073
+ if (!value || !isString(value))
23031
23074
  return true;
23032
23075
  return !!value.match(regex);
23033
23076
  }
@@ -23160,7 +23203,7 @@ Use an identity property function instead: ${ example }.`)];
23160
23203
  if (!isValid(style.visibility, visibilityPattern)) {
23161
23204
  errors.push(new ValidationError('visibility', style.visibility, 'Style visibility must be public or private'));
23162
23205
  }
23163
- if (style.protected !== void 0 && getType(style.protected) !== 'boolean') {
23206
+ if (style.protected !== void 0 && !isBoolean(style.protected)) {
23164
23207
  errors.push(new ValidationError('protected', style.protected, 'Style protection must be true or false'));
23165
23208
  }
23166
23209
  return errors;