@mapbox/mapbox-gl-style-spec 13.28.0 → 14.0.0-beta.2

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 (60) hide show
  1. package/LICENSE.txt +47 -0
  2. package/README.md +7 -0
  3. package/data/extent.js +18 -0
  4. package/deref.js +1 -1
  5. package/diff.js +36 -15
  6. package/dist/index.cjs +5839 -2015
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.es.js +5837 -2011
  9. package/dist/index.es.js.map +1 -1
  10. package/expression/compound_expression.js +2 -1
  11. package/expression/definitions/assertion.js +2 -2
  12. package/expression/definitions/coercion.js +54 -15
  13. package/expression/definitions/comparison.js +2 -0
  14. package/expression/definitions/distance.js +597 -0
  15. package/expression/definitions/format.js +2 -2
  16. package/expression/definitions/image.js +34 -14
  17. package/expression/definitions/index.js +122 -8
  18. package/expression/definitions/interpolate.js +1 -1
  19. package/expression/definitions/match.js +2 -2
  20. package/expression/definitions/within.js +21 -92
  21. package/expression/evaluation_context.js +12 -1
  22. package/expression/index.js +74 -43
  23. package/expression/is_constant.js +19 -1
  24. package/expression/parsing_context.js +20 -16
  25. package/expression/types/formatted.js +2 -2
  26. package/expression/types/resolved_image.js +19 -8
  27. package/expression/types.js +2 -1
  28. package/expression/values.js +25 -0
  29. package/feature_filter/convert.js +1 -1
  30. package/feature_filter/index.js +4 -4
  31. package/flow-typed/cheap-ruler.js +25 -0
  32. package/flow-typed/geojson.js +8 -7
  33. package/flow-typed/gl-matrix.js +4 -2
  34. package/flow-typed/kdbush.js +9 -0
  35. package/function/convert.js +23 -12
  36. package/group_by_layout.js +2 -2
  37. package/migrate/expressions.js +3 -0
  38. package/package.json +6 -3
  39. package/reference/v8.json +1771 -114
  40. package/style-spec.js +4 -3
  41. package/types.js +190 -24
  42. package/util/color.js +31 -0
  43. package/util/geometry_util.js +145 -0
  44. package/util/properties.js +10 -2
  45. package/util/random.js +12 -0
  46. package/validate/validate.js +17 -7
  47. package/validate/validate_array.js +1 -1
  48. package/validate/validate_filter.js +4 -12
  49. package/validate/validate_function.js +2 -2
  50. package/validate/validate_import.js +31 -0
  51. package/validate/validate_layer.js +3 -2
  52. package/validate/validate_lights.js +84 -0
  53. package/validate/validate_model.js +38 -0
  54. package/validate/validate_property.js +18 -4
  55. package/validate/validate_source.js +3 -2
  56. package/validate/validate_style.js +29 -0
  57. package/validate_mapbox_api_supported.js +55 -11
  58. package/validate_style.js +4 -0
  59. package/validate_style.min.js +11 -19
  60. package/visit.js +3 -2
@@ -1,13 +1,21 @@
1
1
  // @flow
2
2
 
3
- import type {StylePropertySpecification} from '../style-spec.js';
3
+ import type {ExpressionSpecification, StylePropertySpecification} from '../style-spec.js';
4
+
5
+ function expressionHasParameter(expression: ?ExpressionSpecification, parameter: string): boolean {
6
+ return !!expression && !!expression.parameters && expression.parameters.indexOf(parameter) > -1;
7
+ }
4
8
 
5
9
  export function supportsPropertyExpression(spec: StylePropertySpecification): boolean {
6
10
  return spec['property-type'] === 'data-driven';
7
11
  }
8
12
 
13
+ export function supportsLightExpression(spec: StylePropertySpecification): boolean {
14
+ return expressionHasParameter(spec.expression, 'measure-light');
15
+ }
16
+
9
17
  export function supportsZoomExpression(spec: StylePropertySpecification): boolean {
10
- return !!spec.expression && spec.expression.parameters.indexOf('zoom') > -1;
18
+ return expressionHasParameter(spec.expression, 'zoom');
11
19
  }
12
20
 
13
21
  export function supportsInterpolation(spec: StylePropertySpecification): boolean {
package/util/random.js ADDED
@@ -0,0 +1,12 @@
1
+ // @flow
2
+
3
+ // Seeded pseudo random generator function
4
+ export function mulberry32(a: number): () => number {
5
+ return function () {
6
+ a |= 0;
7
+ a = (a + 0x6d2b79f5) | 0;
8
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
9
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
10
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
11
+ };
12
+ }
@@ -1,10 +1,12 @@
1
1
  // @flow
2
2
 
3
3
  import extend from '../util/extend.js';
4
+ import ValidationError from '../error/validation_error.js';
4
5
  import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint.js';
5
6
  import {isExpression} from '../expression/index.js';
6
7
  import {isFunction} from '../function/index.js';
7
8
 
9
+ import validateImport from './validate_import.js';
8
10
  import validateFunction from './validate_function.js';
9
11
  import validateExpression from './validate_expression.js';
10
12
  import validateObject from './validate_object.js';
@@ -16,7 +18,9 @@ import validateEnum from './validate_enum.js';
16
18
  import validateFilter from './validate_filter.js';
17
19
  import validateLayer from './validate_layer.js';
18
20
  import validateSource from './validate_source.js';
21
+ import validateModel from './validate_model.js';
19
22
  import validateLight from './validate_light.js';
23
+ import validateLights from './validate_lights.js';
20
24
  import validateTerrain from './validate_terrain.js';
21
25
  import validateFog from './validate_fog.js';
22
26
  import validateString from './validate_string.js';
@@ -26,7 +30,7 @@ import validateProjection from './validate_projection.js';
26
30
 
27
31
  import type {StyleReference} from '../reference/latest.js';
28
32
  import type {StyleSpecification} from '../types.js';
29
- import type ValidationError from '../error/validation_error.js';
33
+ import getType from '../util/get_type.js';
30
34
 
31
35
  const VALIDATORS = {
32
36
  '*'() {
@@ -42,13 +46,16 @@ const VALIDATORS = {
42
46
  'layer': validateLayer,
43
47
  'object': validateObject,
44
48
  'source': validateSource,
49
+ 'model': validateModel,
45
50
  'light': validateLight,
51
+ 'light-3d': validateLights,
46
52
  'terrain': validateTerrain,
47
53
  'fog': validateFog,
48
54
  'string': validateString,
49
55
  'formatted': validateFormatted,
50
56
  'resolvedImage': validateImage,
51
- 'projection': validateProjection
57
+ 'projection': validateProjection,
58
+ 'import': validateImport
52
59
  };
53
60
 
54
61
  // Main recursive validation function. Tracks:
@@ -68,20 +75,23 @@ export type ValidationOptions = {
68
75
  styleSpec: StyleReference;
69
76
  }
70
77
 
71
- export default function validate(options: ValidationOptions): Array<ValidationError> {
78
+ export default function validate(options: ValidationOptions, arrayAsExpression: boolean = false): Array<ValidationError> {
72
79
  const value = options.value;
73
80
  const valueSpec = options.valueSpec;
74
81
  const styleSpec = options.styleSpec;
75
82
 
76
83
  if (valueSpec.expression && isFunction(unbundle(value))) {
77
84
  return validateFunction(options);
78
-
79
85
  } else if (valueSpec.expression && isExpression(deepUnbundle(value))) {
80
86
  return validateExpression(options);
81
-
82
87
  } else if (valueSpec.type && VALIDATORS[valueSpec.type]) {
83
- return VALIDATORS[valueSpec.type](options);
84
-
88
+ const valid = VALIDATORS[valueSpec.type](options);
89
+ if (arrayAsExpression === true && valid.length > 0 && getType(options.value) === "array") {
90
+ // Try to validate as an expression
91
+ return validateExpression(options);
92
+ } else {
93
+ return valid;
94
+ }
85
95
  } else {
86
96
  const valid = validateObject(extend({}, options, {
87
97
  valueSpec: valueSpec.type ? styleSpec[valueSpec.type] : valueSpec
@@ -56,7 +56,7 @@ export default function validateArray(options: Options): Array<ValidationError>
56
56
  style,
57
57
  styleSpec,
58
58
  key: `${key}[${i}]`
59
- }));
59
+ }, true));
60
60
  }
61
61
  return errors;
62
62
  }
@@ -11,7 +11,7 @@ import {isExpressionFilter} from '../feature_filter/index.js';
11
11
  import type {ValidationOptions} from './validate.js';
12
12
 
13
13
  type Options = ValidationOptions & {
14
- layerType: string;
14
+ layerType?: string;
15
15
  }
16
16
 
17
17
  export default function validateFilter(options: Options): Array<ValidationError> {
@@ -28,7 +28,7 @@ export default function validateFilter(options: Options): Array<ValidationError>
28
28
  }
29
29
  }
30
30
 
31
- function validateNonExpressionFilter(options) {
31
+ function validateNonExpressionFilter(options: Options) {
32
32
  const value = options.value;
33
33
  const key = options.key;
34
34
 
@@ -96,12 +96,12 @@ function validateNonExpressionFilter(options) {
96
96
  case 'all':
97
97
  case 'none':
98
98
  for (let i = 1; i < value.length; i++) {
99
- errors = errors.concat(validateNonExpressionFilter({
99
+ errors = errors.concat(validateNonExpressionFilter(({
100
100
  key: `${key}[${i}]`,
101
101
  value: value[i],
102
102
  style: options.style,
103
103
  styleSpec: options.styleSpec
104
- }));
104
+ }: any)));
105
105
  }
106
106
  break;
107
107
 
@@ -114,14 +114,6 @@ function validateNonExpressionFilter(options) {
114
114
  errors.push(new ValidationError(`${key}[1]`, value[1], `string expected, ${type} found`));
115
115
  }
116
116
  break;
117
- case 'within':
118
- type = getType(value[1]);
119
- if (value.length !== 2) {
120
- errors.push(new ValidationError(key, value, `filter array for "${value[0]}" operator must have 2 elements`));
121
- } else if (type !== 'object') {
122
- errors.push(new ValidationError(`${key}[1]`, value[1], `object expected, ${type} found`));
123
- }
124
- break;
125
117
  }
126
118
  return errors;
127
119
  }
@@ -21,7 +21,7 @@ export default function validateFunction(options: ValidationOptions): any {
21
21
  const functionType = unbundle(options.value.type);
22
22
  let stopKeyType;
23
23
  let stopDomainValues: {[string | number]: boolean} = {};
24
- let previousStopDomainValue;
24
+ let previousStopDomainValue: ?mixed;
25
25
  let previousStopDomainZoom;
26
26
 
27
27
  const isZoomFunction = functionType !== 'categorical' && options.value.property === undefined;
@@ -161,7 +161,7 @@ export default function validateFunction(options: ValidationOptions): any {
161
161
  }));
162
162
  }
163
163
 
164
- function validateStopDomainValue(options: ValidationOptions, stop) {
164
+ function validateStopDomainValue(options: ValidationOptions, stop: any) {
165
165
  const type = getType(options.value);
166
166
  const value = unbundle(options.value);
167
167
 
@@ -0,0 +1,31 @@
1
+ // @flow
2
+
3
+ import extend from '../util/extend.js';
4
+ import validateStyle from './validate_style.js';
5
+ import validateObject from './validate_object.js';
6
+ import ValidationError from '../error/validation_error.js';
7
+
8
+ import type {ValidationOptions} from './validate.js';
9
+
10
+ export default function validateImport(options: ValidationOptions): ValidationError[] {
11
+ const {value, styleSpec} = options;
12
+ const {data, ...importSpec} = value;
13
+
14
+ // Preserve __line__ from the value
15
+ Object.defineProperty(importSpec, '__line__', {
16
+ value: value.__line__,
17
+ enumerable: false
18
+ });
19
+
20
+ let errors = validateObject(extend({}, options, {
21
+ value: importSpec,
22
+ valueSpec: styleSpec.import
23
+ }));
24
+
25
+ if (data) {
26
+ const key = `${options.key}.data`;
27
+ errors = errors.concat(validateStyle(data, styleSpec, {key}));
28
+ }
29
+
30
+ return errors;
31
+ }
@@ -58,12 +58,13 @@ export default function validateLayer(options: Options): Array<ValidationError>
58
58
  if (!parent) {
59
59
  if (typeof ref === 'string')
60
60
  errors.push(new ValidationError(key, layer.ref, `ref layer "${ref}" not found`));
61
+ // $FlowFixMe[prop-missing] - ref is not defined on the LayerSpecification subtypes
61
62
  } else if (parent.ref) {
62
63
  errors.push(new ValidationError(key, layer.ref, 'ref cannot reference another ref layer'));
63
64
  } else {
64
65
  type = unbundle(parent.type);
65
66
  }
66
- } else if (!(type === 'background' || type === 'sky')) {
67
+ } else if (!(type === 'background' || type === 'sky' || type === 'slot')) {
67
68
  if (!layer.source) {
68
69
  errors.push(new ValidationError(key, layer, 'missing required property "source"'));
69
70
  } else {
@@ -137,7 +138,7 @@ export default function validateLayer(options: Options): Array<ValidationError>
137
138
  styleSpec: options.styleSpec,
138
139
  objectElementValidators: {
139
140
  '*'(options) {
140
- return validatePaintProperty(extend({layerType: type}, options));
141
+ return validatePaintProperty(extend({layerType: type, layer}, options));
141
142
  }
142
143
  }
143
144
  });
@@ -0,0 +1,84 @@
1
+ // @flow
2
+
3
+ import ValidationError from '../error/validation_error.js';
4
+ import getType from '../util/get_type.js';
5
+ import validate from './validate.js';
6
+
7
+ import type {ValidationOptions} from './validate.js';
8
+
9
+ export default function validateLights(options: ValidationOptions): Array<ValidationError> {
10
+ const light = options.value;
11
+ let errors = [];
12
+
13
+ if (!light) {
14
+ return errors;
15
+ }
16
+
17
+ const type = getType(light);
18
+ if (type !== 'object') {
19
+ errors = errors.concat([new ValidationError('light-3d', light, `object expected, ${type} found`)]);
20
+ return errors;
21
+ }
22
+
23
+ const styleSpec = options.styleSpec;
24
+ const lightSpec = styleSpec['light-3d'];
25
+ const style = options.style;
26
+
27
+ for (const key of ['type', 'id']) {
28
+ if (!(key in light)) {
29
+ errors = errors.concat([new ValidationError('light-3d', light, `missing property ${key} on light`)]);
30
+ return errors;
31
+ }
32
+ }
33
+
34
+ const lightType = `properties_light_${light['type']}`;
35
+ if (!(lightType in styleSpec)) {
36
+ errors = errors.concat([new ValidationError('light-3d', light, `Invalid light type ${light['type']}`)]);
37
+ return errors;
38
+ }
39
+
40
+ const lightPropertySpec = styleSpec[lightType];
41
+
42
+ for (const key in light) {
43
+ if (key === 'properties') {
44
+ const properties = light[key];
45
+ const propertiesType = getType(properties);
46
+ if (propertiesType !== 'object') {
47
+ errors = errors.concat([new ValidationError('properties', properties, `object expected, ${propertiesType} found`)]);
48
+ return errors;
49
+ }
50
+ for (const propertyKey in properties) {
51
+ errors = errors.concat(validate({
52
+ key: propertyKey,
53
+ value: properties[propertyKey],
54
+ valueSpec: lightPropertySpec[propertyKey],
55
+ style,
56
+ styleSpec
57
+ }));
58
+ }
59
+ } else {
60
+ const transitionMatch = key.match(/^(.*)-transition$/);
61
+ if (transitionMatch && lightSpec[transitionMatch[1]] && lightSpec[transitionMatch[1]].transition) {
62
+ errors = errors.concat(validate({
63
+ key,
64
+ value: light[key],
65
+ valueSpec: styleSpec.transition,
66
+ style,
67
+ styleSpec
68
+ }));
69
+ } else if (lightSpec[key]) {
70
+ errors = errors.concat(validate({
71
+ key,
72
+ value: light[key],
73
+ valueSpec: lightSpec[key],
74
+ style,
75
+ styleSpec
76
+ }));
77
+ } else {
78
+ errors = errors.concat([new ValidationError(key, light[key], `unknown property "${key}"`)]);
79
+ }
80
+ }
81
+ }
82
+
83
+ return errors;
84
+ }
@@ -0,0 +1,38 @@
1
+ // @flow
2
+
3
+ import ValidationError from '../error/validation_error.js';
4
+ import getType from '../util/get_type.js';
5
+
6
+ import type {ValidationOptions} from './validate.js';
7
+
8
+ // Allow any URL, use dummy base, if it's a relative URL
9
+ function isValidUrl(str: string): boolean {
10
+ const isRelative = str.indexOf('://') === -1;
11
+ try {
12
+ new URL(str, isRelative ? 'http://example.com' : undefined);
13
+ return true;
14
+ } catch (_) {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ export default function validateModel(options: ValidationOptions): Array<ValidationError> {
20
+ const url = options.value;
21
+ let errors = [];
22
+
23
+ if (!url) {
24
+ return errors;
25
+ }
26
+
27
+ const type = getType(url);
28
+ if (type !== 'string') {
29
+ errors = errors.concat([new ValidationError(options.key, url, `string expected, "${type}" found`)]);
30
+ return errors;
31
+ }
32
+
33
+ if (!isValidUrl(url)) {
34
+ errors = errors.concat([new ValidationError(options.key, url, `invalid url "${url}"`)]);
35
+ }
36
+
37
+ return errors;
38
+ }
@@ -5,18 +5,22 @@ import ValidationError from '../error/validation_error.js';
5
5
  import getType from '../util/get_type.js';
6
6
  import {isFunction} from '../function/index.js';
7
7
  import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint.js';
8
- import {supportsPropertyExpression} from '../util/properties.js';
8
+ import {supportsLightExpression, supportsPropertyExpression, supportsZoomExpression} from '../util/properties.js';
9
+ import {isGlobalPropertyConstant} from '../expression/is_constant.js';
9
10
 
10
11
  import type {ValidationOptions} from './validate.js';
12
+ import {createPropertyExpression} from '../expression/index.js';
11
13
 
12
14
  export type PropertyValidationOptions = ValidationOptions & {
13
15
  objectKey: string;
14
16
  layerType: string;
17
+ layer: Object;
15
18
  }
16
19
 
17
20
  export default function validateProperty(options: PropertyValidationOptions, propertyType: string): Array<ValidationError> {
18
21
  const key = options.key;
19
22
  const style = options.style;
23
+ const layer = options.layer;
20
24
  const styleSpec = options.styleSpec;
21
25
  const value = options.value;
22
26
  const propertyKey = options.objectKey;
@@ -40,23 +44,33 @@ export default function validateProperty(options: PropertyValidationOptions, pro
40
44
  return [new ValidationError(key, value, `unknown property "${propertyKey}"`)];
41
45
  }
42
46
 
43
- let tokenMatch;
47
+ let tokenMatch: ?RegExp$matchResult;
44
48
  if (getType(value) === 'string' && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) {
49
+ const example = `\`{ "type": "identity", "property": ${tokenMatch ? JSON.stringify(tokenMatch[1]) : '"_"'} }\``;
45
50
  return [new ValidationError(
46
51
  key, value,
47
52
  `"${propertyKey}" does not support interpolation syntax\n` +
48
- `Use an identity property function instead: \`{ "type": "identity", "property": ${JSON.stringify(tokenMatch[1])} }\`.`)];
53
+ `Use an identity property function instead: ${example}.`)];
49
54
  }
50
55
 
51
56
  const errors = [];
52
57
 
53
58
  if (options.layerType === 'symbol') {
54
- if (propertyKey === 'text-field' && style && !style.glyphs) {
59
+ if (propertyKey === 'text-field' && style && !style.glyphs && !style.imports) {
55
60
  errors.push(new ValidationError(key, value, 'use of "text-field" requires a style "glyphs" property'));
56
61
  }
57
62
  if (propertyKey === 'text-font' && isFunction(deepUnbundle(value)) && unbundle(value.type) === 'identity') {
58
63
  errors.push(new ValidationError(key, value, '"text-font" does not support identity functions'));
59
64
  }
65
+ } else if (options.layerType === 'model' && propertyType === 'paint' && layer && layer.layout && layer.layout.hasOwnProperty('model-id')) {
66
+ if (supportsPropertyExpression(valueSpec) && (supportsLightExpression(valueSpec) || supportsZoomExpression(valueSpec))) {
67
+ // Performance related style spec limitation: zoom and light expressions are not allowed for e.g. trees.
68
+ const expression = createPropertyExpression(deepUnbundle(value), valueSpec);
69
+ const expressionObj = (expression.value: any).expression || (expression.value: any)._styleExpression.expression;
70
+ if (expressionObj && (!isGlobalPropertyConstant(expressionObj, ['zoom']) || !isGlobalPropertyConstant(expressionObj, ['measure-light']))) {
71
+ errors.push(new ValidationError(key, value, `${propertyKey} does not support zoom or measure-light expressions when the model layer source is vector tile or GeoJSON.`));
72
+ }
73
+ }
60
74
  }
61
75
 
62
76
  return errors.concat(validate({
@@ -8,6 +8,7 @@ import validateExpression from './validate_expression.js';
8
8
  import validateString from './validate_string.js';
9
9
  import getType from '../util/get_type.js';
10
10
 
11
+ import type {StyleReference} from '../reference/latest.js';
11
12
  import type {ValidationOptions} from './validate.js';
12
13
 
13
14
  const objectElementValidators = {
@@ -101,7 +102,7 @@ export default function validateSource(options: ValidationOptions): Array<Valida
101
102
  }
102
103
  }
103
104
 
104
- function getSourceTypeValues(styleSpec) {
105
+ function getSourceTypeValues(styleSpec: StyleReference) {
105
106
  return styleSpec.source.reduce((memo, source) => {
106
107
  const sourceType = styleSpec[source];
107
108
  if (sourceType.type.type === 'enum') {
@@ -111,7 +112,7 @@ function getSourceTypeValues(styleSpec) {
111
112
  }, []);
112
113
  }
113
114
 
114
- function validatePromoteId({key, value}) {
115
+ function validatePromoteId({key, value}: $Shape<ValidationOptions>) {
115
116
  if (getType(value) === 'string') {
116
117
  return validateString({key, value});
117
118
  } else {
@@ -0,0 +1,29 @@
1
+ // @flow
2
+ import validate from './validate.js';
3
+ import latestStyleSpec from '../reference/latest.js';
4
+ import validateGlyphsURL from './validate_glyphs_url.js';
5
+
6
+ import ValidationError from '../error/validation_error.js';
7
+
8
+ import type {ValidationOptions} from './validate.js';
9
+ import type {StyleSpecification} from '../types.js';
10
+
11
+ type StyleValidationOptions = {
12
+ key?: ValidationOptions["key"]
13
+ }
14
+
15
+ export default function validateStyle(style: StyleSpecification, styleSpec: Object = latestStyleSpec, options: StyleValidationOptions = {}): ValidationError[] {
16
+ const errors = validate({
17
+ key: options.key || '',
18
+ value: style,
19
+ valueSpec: styleSpec.$root,
20
+ styleSpec,
21
+ style,
22
+ objectElementValidators: {
23
+ glyphs: validateGlyphsURL,
24
+ '*': () => []
25
+ }
26
+ });
27
+
28
+ return errors;
29
+ }
@@ -36,7 +36,7 @@ function getAllowedKeyErrors(obj: Object, keys: Array<*>, path: ?string): Array<
36
36
  return errors;
37
37
  }
38
38
 
39
- const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem"]);
39
+ const acceptedSourceTypes = new Set(["vector", "raster", "raster-dem", "model", "batched-model"]);
40
40
  function getSourceErrors(source: Object, i: number): Array<ValidationError> {
41
41
  const errors = [];
42
42
 
@@ -68,26 +68,58 @@ function getSourceErrors(source: Object, i: number): Array<ValidationError> {
68
68
  return errors;
69
69
  }
70
70
 
71
- function getSourcesErrors(sources: Object): Array<ValidationError> {
71
+ function getMaxSourcesErrors(sourcesCount: number): Array<ValidationError> {
72
72
  const errors = [];
73
- let count = 0;
73
+ if (sourcesCount > MAX_SOURCES_IN_STYLE) {
74
+ errors.push(new ValidationError('sources', null, `Styles must contain ${MAX_SOURCES_IN_STYLE} or fewer sources`));
75
+ }
76
+ return errors;
77
+ }
78
+
79
+ function getSourcesErrors(sources: Object): {errors: Array<ValidationError>, sourcesCount: number} {
80
+ const errors = [];
81
+ let sourcesCount = 0;
74
82
 
75
83
  Object.keys(sources).forEach((s: string, i: number) => {
76
84
  const sourceErrors = getSourceErrors(sources[s], i);
77
85
 
78
86
  // If source has errors, skip counting
79
87
  if (!sourceErrors.length) {
80
- count = count + getSourceCount(sources[s]);
88
+ sourcesCount = sourcesCount + getSourceCount(sources[s]);
81
89
  }
82
90
 
83
91
  errors.push(...sourceErrors);
84
92
  });
85
93
 
86
- if (count > MAX_SOURCES_IN_STYLE) {
87
- errors.push(new ValidationError('sources', null, `Styles must contain ${MAX_SOURCES_IN_STYLE} or fewer sources`));
88
- }
94
+ return {errors, sourcesCount};
95
+ }
89
96
 
90
- return errors;
97
+ function getImportErrors(imports: Array<Object> = []): {errors: Array<ValidationError>, sourcesCount: number} {
98
+ let errors: Array<ValidationError> = [];
99
+
100
+ let sourcesCount = 0;
101
+ const validateImports = (imports: Array<Object> = []) => {
102
+ for (const importSpec of imports) {
103
+ const style = importSpec.data;
104
+ if (!style) continue;
105
+
106
+ if (style.imports) {
107
+ validateImports(style.imports);
108
+ }
109
+
110
+ errors = errors.concat(getRootErrors(style, Object.keys(v8.$root)));
111
+
112
+ if (style.sources) {
113
+ const sourcesErrors = getSourcesErrors(style.sources);
114
+ sourcesCount += sourcesErrors.sourcesCount;
115
+ errors = errors.concat(sourcesErrors.errors);
116
+ }
117
+ }
118
+ };
119
+
120
+ validateImports(imports);
121
+
122
+ return {errors, sourcesCount};
91
123
  }
92
124
 
93
125
  function getRootErrors(style: Object, specKeys: Array<any>): Array<ValidationError> {
@@ -109,7 +141,9 @@ function getRootErrors(style: Object, specKeys: Array<any>): Array<ValidationErr
109
141
  'created',
110
142
  'modified',
111
143
  'visibility',
112
- 'protected'
144
+ 'protected',
145
+ 'models',
146
+ 'lights'
113
147
  ];
114
148
 
115
149
  const allowedKeyErrors = getAllowedKeyErrors(style, [...specKeys, ...optionalRootProperties]);
@@ -178,10 +212,20 @@ export default function validateMapboxApiSupported(style: Object, styleSpec: Obj
178
212
  let errors = validateStyle(s, styleSpec)
179
213
  .concat(getRootErrors(s, Object.keys(v8.$root)));
180
214
 
215
+ let sourcesCount = 0;
181
216
  if (s.sources) {
182
- errors = errors.concat(getSourcesErrors(s.sources));
217
+ const sourcesErrors = getSourcesErrors(s.sources);
218
+ sourcesCount += sourcesErrors.sourcesCount;
219
+ errors = errors.concat(sourcesErrors.errors);
220
+ }
221
+
222
+ if (s.imports) {
223
+ const importsErrors = getImportErrors(s.imports);
224
+ sourcesCount += importsErrors.sourcesCount;
225
+ errors = errors.concat(importsErrors.errors);
183
226
  }
184
227
 
228
+ errors = errors.concat(getMaxSourcesErrors(sourcesCount));
229
+
185
230
  return errors;
186
231
  }
187
-
package/validate_style.js CHANGED
@@ -36,9 +36,13 @@ export default function validateStyle(style: StyleSpecification | string | Buffe
36
36
 
37
37
  export {
38
38
  validateSource as source,
39
+ validateModel as model,
39
40
  validateLight as light,
40
41
  validateLayer as layer,
41
42
  validateFilter as filter,
43
+ validateLights as lights,
44
+ validateTerrain as terrain,
45
+ validateFog as fog,
42
46
  validatePaintProperty as paintProperty,
43
47
  validateLayoutProperty as layoutProperty
44
48
  } from './validate_style.min.js';