@mapbox/mapbox-gl-style-spec 14.23.0 → 14.24.0-rc.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/composite.ts +5 -3
  2. package/diff.ts +37 -80
  3. package/dist/index.cjs +159 -113
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.es.js +159 -113
  7. package/dist/index.es.js.map +1 -1
  8. package/error/parsing_error.ts +3 -1
  9. package/expression/compound_expression.ts +11 -4
  10. package/expression/definitions/assertion.ts +2 -3
  11. package/expression/definitions/case.ts +1 -1
  12. package/expression/definitions/coercion.ts +4 -4
  13. package/expression/definitions/format.ts +1 -1
  14. package/expression/definitions/image.ts +2 -2
  15. package/expression/definitions/in.ts +1 -1
  16. package/expression/definitions/index.ts +7 -8
  17. package/expression/definitions/interpolate.ts +1 -1
  18. package/expression/definitions/let.ts +4 -2
  19. package/expression/definitions/match.ts +1 -1
  20. package/expression/definitions/step.ts +1 -1
  21. package/expression/is_constant.ts +2 -2
  22. package/expression/parsing_context.ts +18 -4
  23. package/expression/stops.ts +2 -1
  24. package/expression/types/image_variant.ts +5 -3
  25. package/feature_filter/index.ts +7 -30
  26. package/function/index.ts +7 -6
  27. package/migrate/v9.ts +1 -1
  28. package/package.json +1 -1
  29. package/reference/v8.json +20 -0
  30. package/types.ts +5 -1
  31. package/util/color.ts +2 -7
  32. package/util/geometry_util.ts +3 -8
  33. package/util/interpolate.ts +2 -3
  34. package/util/lerp.ts +3 -0
  35. package/util/properties.ts +4 -1
  36. package/validate/validate_enum.ts +2 -2
  37. package/validate/validate_fog.ts +3 -2
  38. package/validate/validate_function.ts +2 -2
  39. package/validate/validate_glyphs_url.ts +2 -2
  40. package/validate/validate_layer.ts +6 -5
  41. package/validate/validate_light.ts +3 -2
  42. package/validate/validate_lights.ts +3 -2
  43. package/validate/validate_model.ts +1 -1
  44. package/validate/validate_object.ts +1 -3
  45. package/validate/validate_property.ts +7 -5
  46. package/validate/validate_rain.ts +2 -1
  47. package/validate/validate_snow.ts +2 -1
  48. package/validate/validate_style.ts +1 -0
  49. package/validate/validate_terrain.ts +3 -2
  50. package/validate_mapbox_api_supported.ts +9 -8
@@ -16,12 +16,12 @@ export default function validateEnum(options: EnumValidatorOptions): ValidationE
16
16
 
17
17
  const errors: ValidationError[] = [];
18
18
  if (Array.isArray(valueSpec.values)) { // <=v7
19
- if (valueSpec.values.indexOf(unbundle(value)) === -1) {
19
+ if (!valueSpec.values.includes(unbundle(value))) {
20
20
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
21
21
  errors.push(new ValidationError(key, value, `expected one of [${valueSpec.values.join(', ')}], ${JSON.stringify(value)} found`));
22
22
  }
23
23
  } else { // >=v8
24
- if (Object.keys(valueSpec.values).indexOf(unbundle(value) as string) === -1) {
24
+ if (!Object.keys(valueSpec.values).includes(unbundle(value) as string)) {
25
25
  errors.push(new ValidationError(key, value, `expected one of [${Object.keys(valueSpec.values).join(', ')}], ${JSON.stringify(value)} found`));
26
26
  }
27
27
  }
@@ -1,6 +1,7 @@
1
1
  import {default as ValidationError, ValidationWarning} from '../error/validation_error';
2
2
  import validate from './validate';
3
3
  import {getType, isObject} from '../util/get_type';
4
+ import {TRANSITION_KEY_RE, USE_THEME_KEY_RE} from '../util/properties';
4
5
 
5
6
  import type {StyleReference} from '../reference/latest';
6
7
  import type {StyleSpecification} from '../types';
@@ -29,8 +30,8 @@ export default function validateFog(options: FogValidatorOptions): ValidationErr
29
30
 
30
31
  let errors: ValidationError[] = [];
31
32
  for (const key in fog) {
32
- const transitionMatch = key.match(/^(.*)-transition$/);
33
- const useThemeMatch = key.match(/^(.*)-use-theme$/);
33
+ const transitionMatch = key.match(TRANSITION_KEY_RE);
34
+ const useThemeMatch = key.match(USE_THEME_KEY_RE);
34
35
 
35
36
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
36
37
  if (useThemeMatch && fogSpec[useThemeMatch[1]]) {
@@ -40,10 +40,10 @@ export default function validateFunction(options: FunctionValidatorOptions): Val
40
40
 
41
41
  const functionValueSpec = options.valueSpec;
42
42
  const functionType = unbundle(value.type);
43
- let stopKeyType;
43
+ let stopKeyType: string | undefined;
44
44
  let stopDomainValues: Partial<Record<string | number, boolean>> = {};
45
45
  let previousStopDomainValue: unknown;
46
- let previousStopDomainZoom;
46
+ let previousStopDomainZoom: number | undefined;
47
47
 
48
48
  const isZoomFunction = functionType !== 'categorical' && value.property === undefined;
49
49
  const isPropertyFunction = !isZoomFunction;
@@ -11,11 +11,11 @@ export default function validateGlyphsUrl({key, value}: GlyphsUrlValidatorOption
11
11
  if (errors.length) return errors;
12
12
 
13
13
  const str = value as string;
14
- if (str.indexOf('{fontstack}') === -1) {
14
+ if (!str.includes('{fontstack}')) {
15
15
  errors.push(new ValidationError(key, value, '"glyphs" url must include a "{fontstack}" token'));
16
16
  }
17
17
 
18
- if (str.indexOf('{range}') === -1) {
18
+ if (!str.includes('{range}')) {
19
19
  errors.push(new ValidationError(key, value, '"glyphs" url must include a "{range}" token'));
20
20
  }
21
21
 
@@ -57,7 +57,7 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
57
57
  }
58
58
  });
59
59
 
60
- let parent;
60
+ let parent: LayerSpecification | undefined;
61
61
 
62
62
  style.layers.forEach((layer) => {
63
63
  if (unbundle(layer.id) === ref) parent = layer;
@@ -66,11 +66,9 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
66
66
  if (!parent) {
67
67
  if (typeof ref === 'string')
68
68
  errors.push(new ValidationError(key, layer.ref, `ref layer "${ref}" not found`));
69
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
70
- } else if (parent.ref) {
69
+ } else if ((parent as LayerSpecification & {ref?: unknown}).ref) {
71
70
  errors.push(new ValidationError(key, layer.ref, 'ref cannot reference another ref layer'));
72
71
  } else {
73
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
74
72
  type = unbundle(parent.type) as string;
75
73
  }
76
74
  } else if (!(type === 'background' || type === 'sky' || type === 'slot')) {
@@ -93,9 +91,12 @@ export default function validateLayer(options: LayerValidatorOptions): Validatio
93
91
  errors.push(new ValidationError(key, layer.source, 'raster-dem source can only be used with layer type \'hillshade\'.'));
94
92
  } else if (sourceType === 'raster-array' && !['raster', 'raster-particle'].includes(type)) {
95
93
  errors.push(new ValidationError(key, layer.source, `raster-array source can only be used with layer type \'raster\'.`));
96
- } else if (type === 'line' && layer.paint && (layer.paint['line-gradient'] || layer.paint['line-trim-offset']) &&
94
+ } else if (type === 'line' && layer.paint && layer.paint['line-gradient'] &&
97
95
  (sourceType === 'geojson' && !(source as GeoJSONSourceSpecification).lineMetrics)) {
98
96
  errors.push(new ValidationError(key, layer, `layer "${layer.id as string}" specifies a line-gradient, which requires the GeoJSON source to have \`lineMetrics\` enabled.`));
97
+ } else if (type === 'line' && layer.paint && layer.paint['line-trim-offset'] &&
98
+ (sourceType === 'geojson' && !(source as GeoJSONSourceSpecification).lineMetrics)) {
99
+ errors.push(new ValidationError(key, layer, `layer "${layer.id as string}" specifies a line-trim-offset, which requires the GeoJSON source to have \`lineMetrics\` enabled.`));
99
100
  } else if (type === 'raster-particle' && sourceType !== 'raster-array') {
100
101
  errors.push(new ValidationError(key, layer.source, `layer "${layer.id as string}" requires a \'raster-array\' source.`));
101
102
  }
@@ -1,6 +1,7 @@
1
1
  import ValidationError from '../error/validation_error';
2
2
  import {getType, isObject} from '../util/get_type';
3
3
  import validate from './validate';
4
+ import {TRANSITION_KEY_RE, USE_THEME_KEY_RE} from '../util/properties';
4
5
 
5
6
  import type {StyleReference} from '../reference/latest';
6
7
  import type {StyleSpecification} from '../types';
@@ -29,8 +30,8 @@ export default function validateLight(options: LightValidatorOptions): Validatio
29
30
 
30
31
  let errors: ValidationError[] = [];
31
32
  for (const key in light) {
32
- const transitionMatch = key.match(/^(.*)-transition$/);
33
- const useThemeMatch = key.match(/^(.*)-use-theme$/);
33
+ const transitionMatch = key.match(TRANSITION_KEY_RE);
34
+ const useThemeMatch = key.match(USE_THEME_KEY_RE);
34
35
 
35
36
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
36
37
  if (useThemeMatch && lightSpec[useThemeMatch[1]]) {
@@ -2,6 +2,7 @@ import {default as ValidationError, ValidationWarning} from '../error/validation
2
2
  import {getType, isObject, isString} from '../util/get_type';
3
3
  import validate from './validate';
4
4
  import {unbundle} from '../util/unbundle_jsonlint';
5
+ import {TRANSITION_KEY_RE, USE_THEME_KEY_RE} from '../util/properties';
5
6
 
6
7
  import type {StyleReference} from '../reference/latest';
7
8
  import type {StyleSpecification} from '../types';
@@ -72,8 +73,8 @@ export default function validateLights(options: LightsValidatorOptions): Validat
72
73
  return errors;
73
74
  }
74
75
  for (const propertyKey in properties) {
75
- const transitionMatch = propertyKey.match(/^(.*)-transition$/);
76
- const useThemeMatch = propertyKey.match(/^(.*)-use-theme$/);
76
+ const transitionMatch = propertyKey.match(TRANSITION_KEY_RE);
77
+ const useThemeMatch = propertyKey.match(USE_THEME_KEY_RE);
77
78
 
78
79
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
79
80
  if (useThemeMatch && lightPropertySpec[useThemeMatch[1]]) {
@@ -6,7 +6,7 @@ import type {StyleSpecification} from '../types';
6
6
 
7
7
  // Allow any URL, use dummy base, if it's a relative URL
8
8
  export function isValidUrl(str: string, allowRelativeUrls: boolean): boolean {
9
- const isRelative = str.indexOf('://') === -1;
9
+ const isRelative = !str.includes('://');
10
10
  try {
11
11
  new URL(str, isRelative && allowRelativeUrls ? 'http://example.com' : undefined);
12
12
  return true;
@@ -45,7 +45,7 @@ export default function validateObject(options: ObjectValidatorOptions): Validat
45
45
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
46
46
  const elementSpec = elementSpecs[elementSpecKey] || elementSpecs['*'];
47
47
 
48
- let validateElement;
48
+ let validateElement: ((options: ObjectElementValidatorOptions, object?: unknown) => ValidationError[]) | undefined;
49
49
  if (elementValidators[elementSpecKey]) {
50
50
  validateElement = elementValidators[elementSpecKey];
51
51
  } else if (elementSpecs[elementSpecKey]) {
@@ -61,11 +61,9 @@ export default function validateObject(options: ObjectValidatorOptions): Validat
61
61
  continue;
62
62
  }
63
63
 
64
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
65
64
  errors = errors.concat(validateElement({
66
65
  key: (key ? `${key}.` : key) + objectKey,
67
66
  value: object[objectKey],
68
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
69
67
  valueSpec: elementSpec,
70
68
  style,
71
69
  styleSpec,
@@ -3,7 +3,7 @@ import {default as ValidationError, ValidationWarning} from '../error/validation
3
3
  import {isString} from '../util/get_type';
4
4
  import {isFunction} from '../function/index';
5
5
  import {unbundle, deepUnbundle} from '../util/unbundle_jsonlint';
6
- import {supportsLightExpression, supportsPropertyExpression, supportsZoomExpression} from '../util/properties';
6
+ import {supportsLightExpression, supportsPropertyExpression, supportsZoomExpression, TRANSITION_KEY_RE, USE_THEME_KEY_RE} from '../util/properties';
7
7
  import {isGlobalPropertyConstant, isFeatureConstant, isStateConstant} from '../expression/is_constant';
8
8
  import {createPropertyExpression, isExpression} from '../expression/index';
9
9
 
@@ -12,6 +12,8 @@ import type {StyleReference} from '../reference/latest';
12
12
  import type {StylePropertySpecification} from '../style-spec';
13
13
  import type {StyleSpecification, LayerSpecification} from '../types';
14
14
 
15
+ const TOKEN_PATTERN_RE = /^{([^}]+)}$/;
16
+
15
17
  export type PropertyValidatorOptions = {
16
18
  key: string;
17
19
  value: unknown;
@@ -34,7 +36,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
34
36
 
35
37
  if (!layerSpec) return [];
36
38
 
37
- const useThemeMatch = propertyKey.match(/^(.*)-use-theme$/);
39
+ const useThemeMatch = propertyKey.match(USE_THEME_KEY_RE);
38
40
  if (useThemeMatch && layerSpec[useThemeMatch[1]]) {
39
41
  if (isExpression(deepUnbundle(value))) {
40
42
  const errors: ValidationError[] = [];
@@ -66,7 +68,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
66
68
  });
67
69
  }
68
70
 
69
- const transitionMatch = propertyKey.match(/^(.*)-transition$/);
71
+ const transitionMatch = propertyKey.match(TRANSITION_KEY_RE);
70
72
  if (propertyType === 'paint' && transitionMatch && layerSpec[transitionMatch[1]] && layerSpec[transitionMatch[1]].transition) {
71
73
  return validate({
72
74
  key,
@@ -84,7 +86,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
84
86
  }
85
87
 
86
88
  let tokenMatch: RegExpExecArray | undefined;
87
- if (isString(value) && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = /^{([^}]+)}$/.exec(value))) {
89
+ if (isString(value) && supportsPropertyExpression(valueSpec) && !valueSpec.tokens && (tokenMatch = TOKEN_PATTERN_RE.exec(value))) {
88
90
  const example = `\`{ "type": "identity", "property": ${tokenMatch ? JSON.stringify(tokenMatch[1]) : '"_"'} }\``;
89
91
  return [new ValidationError(
90
92
  key, value,
@@ -101,7 +103,7 @@ export default function validateProperty(options: PropertyValidatorOptions, prop
101
103
  if (propertyKey === 'text-font' && isFunction(deepUnbundle(value)) && unbundle((value as {type: unknown}).type) === 'identity') {
102
104
  errors.push(new ValidationError(key, value, '"text-font" does not support identity functions'));
103
105
  }
104
- } else if (options.layerType === 'model' && propertyType === 'paint' && layer && layer.layout && layer.layout.hasOwnProperty('model-id')) {
106
+ } else if (options.layerType === 'model' && propertyType === 'paint' && layer && layer.layout && Object.hasOwn(layer.layout, 'model-id')) {
105
107
  if (supportsPropertyExpression(valueSpec) && (supportsLightExpression(valueSpec) || supportsZoomExpression(valueSpec))) {
106
108
  // Performance related style spec limitation: zoom and light expressions are not allowed for e.g. trees.
107
109
  const expression = createPropertyExpression(deepUnbundle(value), valueSpec);
@@ -1,6 +1,7 @@
1
1
  import {default as ValidationError, ValidationWarning} from '../error/validation_error';
2
2
  import validate from './validate';
3
3
  import {getType, isObject} from '../util/get_type';
4
+ import {TRANSITION_KEY_RE} from '../util/properties';
4
5
 
5
6
  import type {StyleReference} from '../reference/latest';
6
7
  import type {StyleSpecification} from '../types';
@@ -29,7 +30,7 @@ export default function validateRain(options: RainValidatorOptions): ValidationE
29
30
 
30
31
  let errors: ValidationError[] = [];
31
32
  for (const key in rain) {
32
- const transitionMatch = key.match(/^(.*)-transition$/);
33
+ const transitionMatch = key.match(TRANSITION_KEY_RE);
33
34
 
34
35
  if (transitionMatch && rainSpec[transitionMatch[1]] && rainSpec[transitionMatch[1]].transition) {
35
36
  errors = errors.concat(validate({
@@ -1,6 +1,7 @@
1
1
  import {default as ValidationError, ValidationWarning} from '../error/validation_error';
2
2
  import validate from './validate';
3
3
  import {getType, isObject} from '../util/get_type';
4
+ import {TRANSITION_KEY_RE} from '../util/properties';
4
5
 
5
6
  import type {StyleReference} from '../reference/latest';
6
7
  import type {StyleSpecification} from '../types';
@@ -29,7 +30,7 @@ export default function validateSnow(options: SnowValidatorOptions): ValidationE
29
30
 
30
31
  let errors: ValidationError[] = [];
31
32
  for (const key in snow) {
32
- const transitionMatch = key.match(/^(.*)-transition$/);
33
+ const transitionMatch = key.match(TRANSITION_KEY_RE);
33
34
 
34
35
  if (transitionMatch && snowSpec[transitionMatch[1]] && snowSpec[transitionMatch[1]].transition) {
35
36
  errors = errors.concat(validate({
@@ -15,6 +15,7 @@ export default function validateStyle(style: unknown, styleSpec: StyleReference
15
15
  value: style,
16
16
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
17
17
  valueSpec: Object.assign(
18
+ {},
18
19
  styleSpec.$root,
19
20
  // Skip validation of the root properties that are not defined in the style spec (e.g. 'owner').
20
21
  {'*': {type: '*'}},
@@ -2,6 +2,7 @@ import {default as ValidationError, ValidationWarning} from '../error/validation
2
2
  import validate from './validate';
3
3
  import {getType, isObject, isString} from '../util/get_type';
4
4
  import {unbundle} from '../util/unbundle_jsonlint';
5
+ import {TRANSITION_KEY_RE, USE_THEME_KEY_RE} from '../util/properties';
5
6
 
6
7
  import type {StyleReference} from '../reference/latest';
7
8
  import type {StyleSpecification} from '../types';
@@ -31,8 +32,8 @@ export default function validateTerrain(options: TerrainValidatorOptions): Valid
31
32
 
32
33
  let errors: ValidationError[] = [];
33
34
  for (const key in terrain) {
34
- const transitionMatch = key.match(/^(.*)-transition$/);
35
- const useThemeMatch = key.match(/^(.*)-use-theme$/);
35
+ const transitionMatch = key.match(TRANSITION_KEY_RE);
36
+ const useThemeMatch = key.match(USE_THEME_KEY_RE);
36
37
 
37
38
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
38
39
  if (useThemeMatch && terrainSpec[useThemeMatch[1]]) {
@@ -13,6 +13,11 @@ import type {
13
13
  ImportSpecification
14
14
  } from './types';
15
15
 
16
+ const MAPBOX_SOURCE_URL_RE = /^mapbox:\/\/([^/]*)$/;
17
+ const MAPBOX_GLYPH_URL_RE = /^mapbox:\/\/fonts\/([^/]*)\/{fontstack}\/{range}.pbf$/;
18
+ const MAPBOX_SPRITE_URL_RE = /^mapbox:\/\/sprites\/([^/]*)\/([^/]*)\/?([^/]*)?$/;
19
+ const VISIBILITY_RE = /^(public|private)$/;
20
+
16
21
  type MapboxStyleSpecification = StyleSpecification & {
17
22
  visibility?: 'public' | 'private';
18
23
  protected?: boolean;
@@ -63,8 +68,7 @@ function getSourceErrors(source: SourceSpecification, i: number): Array<Validati
63
68
  * mapbox://penny.abcd1234
64
69
  * mapbox://mapbox.abcd1234,penny.abcd1234
65
70
  */
66
- const sourceUrlPattern = /^mapbox:\/\/([^/]*)$/;
67
- if (!('url' in source) || !isValid(source.url, sourceUrlPattern)) {
71
+ if (!('url' in source) || !isValid(source.url, MAPBOX_SOURCE_URL_RE)) {
68
72
  errors.push(new ValidationError(`sources[${i}].url`, (source as {url?: string}).url, 'Expected a valid Mapbox tileset url'));
69
73
  }
70
74
 
@@ -166,8 +170,7 @@ function getRootErrors(style: MapboxStyleSpecification, specKeys: string[]): Arr
166
170
  * mapbox://fonts/penny/{fontstack}/{range}.pbf
167
171
  * mapbox://fonts/mapbox/{fontstack}/{range}.pbf
168
172
  */
169
- const glyphUrlPattern = /^mapbox:\/\/fonts\/([^/]*)\/{fontstack}\/{range}.pbf$/;
170
- if (!isValid(style.glyphs, glyphUrlPattern)) {
173
+ if (!isValid(style.glyphs, MAPBOX_GLYPH_URL_RE)) {
171
174
  errors.push(new ValidationError('glyphs', style.glyphs, 'Styles must reference glyphs hosted by Mapbox'));
172
175
  }
173
176
 
@@ -177,8 +180,7 @@ function getRootErrors(style: MapboxStyleSpecification, specKeys: string[]): Arr
177
180
  * mapbox://sprites/mapbox/abcd1234/draft
178
181
  * mapbox://sprites/cyrus/abcd1234/abcd1234
179
182
  */
180
- const spriteUrlPattern = /^mapbox:\/\/sprites\/([^/]*)\/([^/]*)\/?([^/]*)?$/;
181
- if (!isValid(style.sprite, spriteUrlPattern)) {
183
+ if (!isValid(style.sprite, MAPBOX_SPRITE_URL_RE)) {
182
184
  errors.push(new ValidationError('sprite', style.sprite, 'Styles must reference sprites hosted by Mapbox'));
183
185
  }
184
186
 
@@ -187,8 +189,7 @@ function getRootErrors(style: MapboxStyleSpecification, specKeys: string[]): Arr
187
189
  * "private"
188
190
  * "public"
189
191
  */
190
- const visibilityPattern = /^(public|private)$/;
191
- if (!isValid(style.visibility, visibilityPattern)) {
192
+ if (!isValid(style.visibility, VISIBILITY_RE)) {
192
193
  errors.push(new ValidationError('visibility', style.visibility, 'Style visibility must be public or private'));
193
194
  }
194
195