@mapbox/mapbox-gl-style-spec 14.0.0 → 14.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,17 +21,23 @@ class CompoundExpression implements Expression {
21
21
  type: Type;
22
22
  _evaluate: Evaluate;
23
23
  args: Array<Expression>;
24
+ _overloadIndex: number;
24
25
 
25
26
  static definitions: {[_: string]: Definition };
26
27
 
27
- constructor(name: string, type: Type, evaluate: Evaluate, args: Array<Expression>) {
28
+ constructor(name: string, type: Type, evaluate: Evaluate, args: Array<Expression>, overloadIndex: number) {
28
29
  this.name = name;
29
30
  this.type = type;
30
31
  this._evaluate = evaluate;
31
32
  this.args = args;
33
+ this._overloadIndex = overloadIndex;
32
34
  }
33
35
 
34
36
  evaluate(ctx: EvaluationContext): Value {
37
+ if (!this._evaluate) { // restore evaluate function after transfer between threads
38
+ const definition = CompoundExpression.definitions[this.name];
39
+ this._evaluate = Array.isArray(definition) ? definition[2] : definition.overloads[this._overloadIndex][1];
40
+ }
35
41
  return this._evaluate(ctx, this.args);
36
42
  }
37
43
 
@@ -62,14 +68,18 @@ class CompoundExpression implements Expression {
62
68
  [[definition[1], definition[2]]] :
63
69
  definition.overloads;
64
70
 
65
- const overloads = availableOverloads.filter(([signature]) => (
66
- !Array.isArray(signature) || // varags
67
- signature.length === args.length - 1 // correct param count
68
- ));
71
+ const overloadParams = [];
69
72
 
70
73
  let signatureContext: ParsingContext = (null: any);
71
74
 
72
- for (const [params, evaluate] of overloads) {
75
+ let overloadIndex = -1;
76
+
77
+ for (const [params, evaluate] of availableOverloads) {
78
+ if (Array.isArray(params) && params.length !== args.length - 1) continue; // param count doesn't match
79
+
80
+ overloadParams.push(params);
81
+ overloadIndex++;
82
+
73
83
  // Use a fresh context for each attempted signature so that, if
74
84
  // we eventually succeed, we haven't polluted `context.errors`.
75
85
  signatureContext = new ParsingContext(context.registry, context.path, null, context.scope, undefined, context.options);
@@ -111,19 +121,17 @@ class CompoundExpression implements Expression {
111
121
  }
112
122
 
113
123
  if (signatureContext.errors.length === 0) {
114
- return new CompoundExpression(op, type, evaluate, parsedArgs);
124
+ return new CompoundExpression(op, type, evaluate, parsedArgs, overloadIndex);
115
125
  }
116
126
  }
117
127
 
118
128
  assert(!signatureContext || signatureContext.errors.length > 0);
119
129
 
120
- if (overloads.length === 1) {
130
+ if (overloadParams.length === 1) {
121
131
  context.errors.push(...signatureContext.errors);
122
132
  } else {
123
- const expected = overloads.length ? overloads : availableOverloads;
124
- const signatures = expected
125
- .map(([params]) => stringifySignature(params))
126
- .join(' | ');
133
+ const expected = overloadParams.length ? overloadParams : availableOverloads.map(([params]) => params);
134
+ const signatures = expected.map(stringifySignature).join(' | ');
127
135
 
128
136
  const actualTypes = [];
129
137
  // For error message, re-parse arguments without trying to
@@ -145,12 +145,57 @@ function get(key: string, obj: {[string]: any}) {
145
145
  return typeof v === 'undefined' ? null : v;
146
146
  }
147
147
 
148
+ function coerceValue(type: 'string' | 'number' | 'boolean' | 'color', value: any): any {
149
+ switch (type) {
150
+ case 'string': return String(value);
151
+ case 'number': return +value;
152
+ case 'boolean': return !!value;
153
+ case 'color': return Color.parse(value);
154
+ }
155
+ return value;
156
+ }
157
+
158
+ function clampToAllowedNumber(value: number, min: number | void, max: number | void, step: number | void): number {
159
+ if (step !== undefined) {
160
+ value = step * Math.round(value / step);
161
+ }
162
+ if (min !== undefined && value < min) {
163
+ value = min;
164
+ }
165
+ if (max !== undefined && value > max) {
166
+ value = max;
167
+ }
168
+ return value;
169
+ }
170
+
148
171
  function getConfig(ctx: EvaluationContext, key: string, scope: string) {
149
172
  if (scope.length) {
150
173
  key += `\u{1f}${scope}`;
151
174
  }
152
- const v = ctx.getConfig(key);
153
- return v ? v.evaluate(ctx) : null;
175
+ const config = ctx.getConfig(key);
176
+ if (!config) return null;
177
+
178
+ const {type, value, values, minValue, maxValue, stepValue} = config;
179
+
180
+ const defaultValue = config.default.evaluate(ctx);
181
+ let result = value ? value.evaluate(ctx) : defaultValue;
182
+
183
+ if (type) result = coerceValue(type, result);
184
+
185
+ if (value !== undefined && result !== undefined && values && !values.includes(result)) {
186
+ result = defaultValue;
187
+ if (type) result = coerceValue(type, result);
188
+ }
189
+
190
+ if (result !== undefined && (minValue !== undefined || maxValue !== undefined || stepValue !== undefined)) {
191
+ if (typeof result === 'number') {
192
+ result = clampToAllowedNumber(result, minValue, maxValue, stepValue);
193
+ } else if (Array.isArray(result)) {
194
+ result = result.map((item) => typeof item === 'number' ? clampToAllowedNumber(item, minValue, maxValue, stepValue) : item);
195
+ }
196
+ }
197
+
198
+ return result;
154
199
  }
155
200
 
156
201
  function binarySearch(v: any, a: {[number]: any}, i: number, j: number) {
@@ -1,13 +1,13 @@
1
1
  // @flow
2
2
 
3
3
  import {Color} from './values.js';
4
- import type {Expression} from './expression.js';
5
4
 
6
5
  import type Point from '@mapbox/point-geometry';
7
6
  import type {FormattedSection} from './types/formatted.js';
8
7
  import type {GlobalProperties, Feature, FeatureState} from './index.js';
9
8
  import type {CanonicalTileID} from '../../source/tile_id.js';
10
9
  import type {FeatureDistanceData} from '../feature_filter/index.js';
10
+ import type {ConfigOptions, ConfigOptionValue} from '../../style/properties.js';
11
11
 
12
12
  const geometryTypes = ['Unknown', 'Point', 'LineString', 'Polygon'];
13
13
 
@@ -20,11 +20,11 @@ class EvaluationContext {
20
20
  canonical: null | CanonicalTileID;
21
21
  featureTileCoord: ?Point;
22
22
  featureDistanceData: ?FeatureDistanceData;
23
- options: ?Map<string, Expression>;
23
+ options: ?ConfigOptions;
24
24
 
25
25
  _parseColorCache: {[_: string]: ?Color};
26
26
 
27
- constructor(options?: ?Map<string, Expression>) {
27
+ constructor(options?: ?ConfigOptions) {
28
28
  this.globals = (null: any);
29
29
  this.feature = null;
30
30
  this.featureState = null;
@@ -92,7 +92,7 @@ class EvaluationContext {
92
92
  return cached;
93
93
  }
94
94
 
95
- getConfig(id: string): ?Expression {
95
+ getConfig(id: string): ?ConfigOptionValue {
96
96
  return this.options ? this.options.get(id) : null;
97
97
  }
98
98
  }
@@ -33,6 +33,7 @@ import type {FormattedSection} from './types/formatted.js';
33
33
  import type Point from '@mapbox/point-geometry';
34
34
  import type {CanonicalTileID} from '../../source/tile_id.js';
35
35
  import type {FeatureDistanceData} from '../feature_filter/index.js';
36
+ import type {ConfigOptions} from '../../style/properties.js';
36
37
 
37
38
  export interface Feature {
38
39
  +type: 1 | 2 | 3 | 'Unknown' | 'Point' | 'LineString' | 'Polygon';
@@ -64,7 +65,7 @@ export class StyleExpression {
64
65
  _warningHistory: {[key: string]: boolean};
65
66
  _enumValues: ?{[_: string]: any};
66
67
 
67
- constructor(expression: Expression, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>) {
68
+ constructor(expression: Expression, propertySpec: ?StylePropertySpecification, options?: ?ConfigOptions) {
68
69
  this.expression = expression;
69
70
  this._warningHistory = {};
70
71
  this._evaluator = new EvaluationContext(options);
@@ -131,7 +132,7 @@ export function isExpression(expression: mixed): boolean {
131
132
  *
132
133
  * @private
133
134
  */
134
- export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, options?: ?Map<string, Expression>): Result<StyleExpression, Array<ParsingError>> {
135
+ export function createExpression(expression: mixed, propertySpec: ?StylePropertySpecification, options?: ?ConfigOptions): Result<StyleExpression, Array<ParsingError>> {
135
136
  const parser = new ParsingContext(definitions, [], propertySpec ? getExpectedType(propertySpec) : undefined, undefined, undefined, options);
136
137
 
137
138
  // For string-valued properties, coerce to string at the top level rather than asserting.
@@ -248,7 +249,7 @@ export type StylePropertyExpression =
248
249
  | CameraExpression
249
250
  | CompositeExpression;
250
251
 
251
- export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, options?: ?Map<string, Expression>): Result<StylePropertyExpression, Array<ParsingError>> {
252
+ export function createPropertyExpression(expression: mixed, propertySpec: StylePropertySpecification, options?: ?ConfigOptions): Result<StylePropertyExpression, Array<ParsingError>> {
252
253
  expression = createExpression(expression, propertySpec, options);
253
254
  if (expression.result === 'error') {
254
255
  return expression;
@@ -330,7 +331,7 @@ export class StylePropertyFunction<T> {
330
331
  }
331
332
  }
332
333
 
333
- export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification, options?: ?Map<string, Expression>): StylePropertyExpression {
334
+ export function normalizePropertyExpression<T>(value: PropertyValueSpecification<T>, specification: StylePropertySpecification, options?: ?ConfigOptions): StylePropertyExpression {
334
335
  if (isFunction(value)) {
335
336
  return (new StylePropertyFunction(value, specification): any);
336
337
 
@@ -16,6 +16,7 @@ import Var from './definitions/var.js';
16
16
 
17
17
  import type {Expression, ExpressionRegistry} from './expression.js';
18
18
  import type {Type} from './types.js';
19
+ import type {ConfigOptions} from '../../style/properties.js';
19
20
 
20
21
  /**
21
22
  * State associated parsing at a given point in an expression tree.
@@ -27,7 +28,7 @@ class ParsingContext {
27
28
  key: string;
28
29
  scope: Scope;
29
30
  errors: Array<ParsingError>;
30
- options: ?Map<string, Expression>;
31
+ options: ?ConfigOptions;
31
32
 
32
33
  // The expected type of this expression. Provided only to allow Expression
33
34
  // implementations to infer argument types: Expression#parse() need not
@@ -41,7 +42,7 @@ class ParsingContext {
41
42
  expectedType: ?Type,
42
43
  scope: Scope = new Scope(),
43
44
  errors: Array<ParsingError> = [],
44
- options?: ?Map<string, Expression>
45
+ options?: ?ConfigOptions
45
46
  ) {
46
47
  this.registry = registry;
47
48
  this.path = path;
@@ -9,6 +9,7 @@ declare module "sinon" {
9
9
  getCall(i: number): SpyCall
10
10
  };
11
11
  declare type Stub = {
12
+ returns(fn: mixed): Spy,
12
13
  callsFake(fn: mixed): Spy
13
14
  };
14
15
  declare class FakeServer {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mapbox/mapbox-gl-style-spec",
3
3
  "description": "a specification for mapbox gl styles",
4
- "version": "14.0.0",
4
+ "version": "14.1.0",
5
5
  "author": "Mapbox",
6
6
  "keywords": [
7
7
  "mapbox",