@mapbox/mapbox-gl-style-spec 14.10.0 → 14.11.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.
- package/composite.ts +1 -0
- package/dist/index.cjs +467 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +76 -39
- package/dist/index.es.js +467 -191
- package/dist/index.es.js.map +1 -1
- package/expression/compound_expression.ts +5 -7
- package/expression/definitions/at.ts +5 -19
- package/expression/definitions/at_interpolated.ts +80 -0
- package/expression/definitions/coercion.ts +1 -5
- package/expression/definitions/comparison.ts +2 -8
- package/expression/definitions/config.ts +1 -4
- package/expression/definitions/distance.ts +28 -43
- package/expression/definitions/format.ts +4 -9
- package/expression/definitions/image.ts +126 -73
- package/expression/definitions/index.ts +3 -2
- package/expression/definitions/interpolate.ts +28 -80
- package/expression/definitions/let.ts +2 -7
- package/expression/definitions/literal.ts +1 -2
- package/expression/definitions/match.ts +3 -12
- package/expression/definitions/step.ts +1 -5
- package/expression/evaluation_context.ts +3 -3
- package/expression/index.ts +12 -18
- package/expression/parsing_context.ts +3 -7
- package/expression/types/formatted.ts +7 -3
- package/expression/types/image_id.ts +61 -0
- package/expression/types/image_variant.ts +79 -0
- package/expression/types/resolved_image.ts +44 -53
- package/format.ts +1 -0
- package/function/index.ts +1 -0
- package/migrate/v8.ts +1 -0
- package/migrate/v9.ts +1 -0
- package/migrate.ts +1 -0
- package/package.json +1 -1
- package/read_style.ts +1 -0
- package/reference/v8.json +154 -7
- package/types/brand.ts +4 -0
- package/types.ts +30 -2
- package/util/color.ts +1 -1
- package/validate/validate.ts +6 -1
- package/validate/validate_iconset.ts +40 -0
- package/validate/validate_layer.ts +1 -5
- package/validate/validate_lights.ts +3 -2
- package/validate/validate_object.ts +2 -0
- package/validate/validate_style.ts +0 -1
- package/expression/types/image_id_with_options.ts +0 -58
|
@@ -1,56 +1,71 @@
|
|
|
1
|
-
import {ColorType, ResolvedImageType, StringType} from '../types';
|
|
2
1
|
import ResolvedImage from '../types/resolved_image';
|
|
2
|
+
import {ImageId} from '../types/image_id';
|
|
3
|
+
import {ColorType, ResolvedImageType, StringType} from '../types';
|
|
3
4
|
|
|
4
5
|
import type Color from '../../util/color';
|
|
5
|
-
import type {Expression, SerializedExpression} from '../expression';
|
|
6
|
-
import type EvaluationContext from '../evaluation_context';
|
|
7
6
|
import type ParsingContext from '../parsing_context';
|
|
7
|
+
import type EvaluationContext from '../evaluation_context';
|
|
8
8
|
import type {Type} from '../types';
|
|
9
|
+
import type {Expression, SerializedExpression} from '../expression';
|
|
9
10
|
|
|
10
11
|
export type ImageParams = Record<string, Expression>;
|
|
12
|
+
export type IconsetParams = {id: string};
|
|
11
13
|
|
|
12
14
|
export type ImageOptions = {
|
|
13
|
-
params
|
|
15
|
+
params?: ImageParams;
|
|
16
|
+
iconset?: IconsetParams;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
type SerializedImageOptions = {
|
|
20
|
+
params?: Record<string, SerializedExpression>;
|
|
21
|
+
iconset?: IconsetParams;
|
|
22
|
+
};
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
function isImageOptions(value: unknown): value is ImageOptions {
|
|
25
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export default class ImageExpression implements Expression {
|
|
25
29
|
type: Type;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
namePrimary: Expression;
|
|
32
|
+
paramsPrimary?: ImageParams;
|
|
33
|
+
iconsetIdPrimary?: string;
|
|
34
|
+
|
|
35
|
+
nameSecondary?: Expression;
|
|
36
|
+
paramsSecondary?: ImageParams;
|
|
37
|
+
iconsetIdSecondary?: string;
|
|
30
38
|
|
|
31
39
|
_imageWarnHistory: Record<string, boolean> = {};
|
|
32
40
|
|
|
33
41
|
constructor(
|
|
34
42
|
inputPrimary: Expression,
|
|
35
43
|
inputSecondary?: Expression | null,
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
inputPrimaryOptions?: ImageOptions,
|
|
45
|
+
inputSecondaryOptions?: ImageOptions
|
|
38
46
|
) {
|
|
39
47
|
this.type = ResolvedImageType;
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
this.namePrimary = inputPrimary;
|
|
49
|
+
this.nameSecondary = inputSecondary;
|
|
50
|
+
|
|
51
|
+
if (inputPrimaryOptions) {
|
|
52
|
+
this.paramsPrimary = inputPrimaryOptions.params;
|
|
53
|
+
this.iconsetIdPrimary = inputPrimaryOptions.iconset ? inputPrimaryOptions.iconset.id : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (inputSecondaryOptions) {
|
|
57
|
+
this.paramsSecondary = inputSecondaryOptions.params;
|
|
58
|
+
this.iconsetIdSecondary = inputSecondaryOptions.iconset ? inputSecondaryOptions.iconset.id : undefined;
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
|
|
46
|
-
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | null |
|
|
62
|
+
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression | null | void {
|
|
47
63
|
if (args.length < 2) {
|
|
48
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Expression'.
|
|
49
64
|
return context.error(`Expected two or more arguments.`);
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
let nextArgId = 1;
|
|
53
|
-
const imageExpression: Array<{image: Expression, options
|
|
68
|
+
const imageExpression: Array<{image: Expression, options?: ImageOptions}> = [];
|
|
54
69
|
|
|
55
70
|
function tryParseImage() {
|
|
56
71
|
if (nextArgId < args.length) {
|
|
@@ -60,7 +75,7 @@ export default class ImageExpression implements Expression {
|
|
|
60
75
|
return false;
|
|
61
76
|
}
|
|
62
77
|
|
|
63
|
-
imageExpression.push({image: imageName, options:
|
|
78
|
+
imageExpression.push({image: imageName, options: {}});
|
|
64
79
|
return true;
|
|
65
80
|
}
|
|
66
81
|
|
|
@@ -69,45 +84,62 @@ export default class ImageExpression implements Expression {
|
|
|
69
84
|
|
|
70
85
|
function tryParseOptions() {
|
|
71
86
|
if (nextArgId < args.length) {
|
|
72
|
-
|
|
87
|
+
const options = args[nextArgId];
|
|
88
|
+
if (!isImageOptions(options)) {
|
|
73
89
|
return true;
|
|
74
90
|
}
|
|
75
91
|
|
|
76
|
-
const params =
|
|
77
|
-
|
|
92
|
+
const params = options.params;
|
|
93
|
+
const iconset = options.iconset;
|
|
78
94
|
const optionsContext = context.concat(nextArgId);
|
|
79
95
|
|
|
80
|
-
if (!params) {
|
|
96
|
+
if (!params && !iconset) {
|
|
81
97
|
nextArgId++;
|
|
82
98
|
return true;
|
|
83
99
|
}
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
// Parse the image options params as expressions
|
|
102
|
+
if (params) {
|
|
103
|
+
if (typeof params !== 'object' || params.constructor !== Object) {
|
|
104
|
+
optionsContext.error(`Image options \"params\" should be an object`);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
89
107
|
|
|
90
|
-
|
|
108
|
+
const parsedParams: ImageParams = {};
|
|
109
|
+
const childContext = optionsContext.concat(undefined, 'params');
|
|
110
|
+
for (const key in params) {
|
|
111
|
+
if (!key) {
|
|
112
|
+
childContext.error(`Image parameter name should be non-empty`);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
91
115
|
|
|
92
|
-
|
|
116
|
+
const value = childContext.concat(undefined, key).parse(params[key], undefined, ColorType, undefined, {typeAnnotation: 'coerce'});
|
|
117
|
+
if (!value) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
93
120
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
121
|
+
parsedParams[key] = value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
imageExpression[imageExpression.length - 1].options.params = parsedParams;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Validate the iconset image options
|
|
128
|
+
if (iconset) {
|
|
129
|
+
if (typeof iconset !== 'object' || iconset.constructor !== Object) {
|
|
130
|
+
optionsContext.error(`Image options \"iconset\" should be an object`);
|
|
97
131
|
return false;
|
|
98
132
|
}
|
|
99
133
|
|
|
100
|
-
|
|
101
|
-
|
|
134
|
+
if (!iconset.id) {
|
|
135
|
+
optionsContext.error(`Image options \"iconset\" should have an \"id\" property`);
|
|
102
136
|
return false;
|
|
103
137
|
}
|
|
104
138
|
|
|
105
|
-
|
|
139
|
+
imageExpression[imageExpression.length - 1].options.iconset = iconset;
|
|
106
140
|
}
|
|
107
141
|
|
|
108
|
-
imageExpression[imageExpression.length - 1].options = parsed;
|
|
109
142
|
nextArgId++;
|
|
110
|
-
|
|
111
143
|
return true;
|
|
112
144
|
}
|
|
113
145
|
|
|
@@ -163,17 +195,30 @@ export default class ImageExpression implements Expression {
|
|
|
163
195
|
}
|
|
164
196
|
|
|
165
197
|
evaluate(ctx: EvaluationContext): null | ResolvedImage {
|
|
198
|
+
const primaryId = {
|
|
199
|
+
name: this.namePrimary.evaluate(ctx),
|
|
200
|
+
iconsetId: this.iconsetIdPrimary
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const secondaryId = this.nameSecondary ? {
|
|
204
|
+
name: this.nameSecondary.evaluate(ctx),
|
|
205
|
+
iconsetId: this.iconsetIdSecondary
|
|
206
|
+
} : undefined;
|
|
207
|
+
|
|
166
208
|
const value = ResolvedImage.build(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
this.
|
|
209
|
+
primaryId,
|
|
210
|
+
secondaryId,
|
|
211
|
+
this.paramsPrimary ? this.evaluateParams(ctx, this.paramsPrimary) : undefined,
|
|
212
|
+
this.paramsSecondary ? this.evaluateParams(ctx, this.paramsSecondary) : undefined
|
|
171
213
|
);
|
|
214
|
+
|
|
172
215
|
if (value && ctx.availableImages) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (value.
|
|
176
|
-
|
|
216
|
+
const primaryId = value.getPrimary().id;
|
|
217
|
+
value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, primaryId));
|
|
218
|
+
if (value.available) {
|
|
219
|
+
// If there's a secondary variant, only mark it available if both are present
|
|
220
|
+
const secondaryId = value.getSecondary() ? value.getSecondary().id : null;
|
|
221
|
+
if (secondaryId) value.available = ctx.availableImages.some((id) => ImageId.isEqual(id, secondaryId));
|
|
177
222
|
}
|
|
178
223
|
}
|
|
179
224
|
|
|
@@ -181,20 +226,22 @@ export default class ImageExpression implements Expression {
|
|
|
181
226
|
}
|
|
182
227
|
|
|
183
228
|
eachChild(fn: (_: Expression) => void) {
|
|
184
|
-
fn(this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
229
|
+
fn(this.namePrimary);
|
|
230
|
+
|
|
231
|
+
if (this.paramsPrimary) {
|
|
232
|
+
for (const key in this.paramsPrimary) {
|
|
233
|
+
if (this.paramsPrimary[key]) {
|
|
234
|
+
fn(this.paramsPrimary[key]);
|
|
189
235
|
}
|
|
190
236
|
}
|
|
191
237
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
238
|
+
|
|
239
|
+
if (this.nameSecondary) {
|
|
240
|
+
fn(this.nameSecondary);
|
|
241
|
+
if (this.paramsSecondary) {
|
|
242
|
+
for (const key in this.paramsSecondary) {
|
|
243
|
+
if (this.paramsSecondary[key]) {
|
|
244
|
+
fn(this.paramsSecondary[key]);
|
|
198
245
|
}
|
|
199
246
|
}
|
|
200
247
|
}
|
|
@@ -206,33 +253,39 @@ export default class ImageExpression implements Expression {
|
|
|
206
253
|
return false;
|
|
207
254
|
}
|
|
208
255
|
|
|
209
|
-
|
|
210
|
-
const result:
|
|
256
|
+
serializeOptions(params: ImageParams, iconsetId: string): SerializedImageOptions | undefined {
|
|
257
|
+
const result: SerializedImageOptions = {};
|
|
258
|
+
|
|
259
|
+
if (iconsetId) {
|
|
260
|
+
result.iconset = {id: iconsetId};
|
|
261
|
+
}
|
|
262
|
+
|
|
211
263
|
if (params) {
|
|
264
|
+
result.params = {};
|
|
212
265
|
for (const key in params) {
|
|
213
266
|
if (params[key]) {
|
|
214
|
-
result[key] = params[key].serialize();
|
|
267
|
+
result.params[key] = params[key].serialize();
|
|
215
268
|
}
|
|
216
269
|
}
|
|
217
|
-
} else {
|
|
218
|
-
return undefined;
|
|
219
270
|
}
|
|
220
271
|
|
|
221
|
-
return
|
|
272
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
222
273
|
}
|
|
223
274
|
|
|
224
275
|
serialize(): SerializedExpression {
|
|
225
|
-
const serialized: SerializedExpression = [
|
|
276
|
+
const serialized: SerializedExpression = ['image', this.namePrimary.serialize()];
|
|
226
277
|
|
|
227
|
-
if (this.
|
|
228
|
-
|
|
278
|
+
if (this.paramsPrimary || this.iconsetIdPrimary) {
|
|
279
|
+
const options = this.serializeOptions(this.paramsPrimary, this.iconsetIdPrimary);
|
|
280
|
+
if (options) serialized.push(options);
|
|
229
281
|
}
|
|
230
282
|
|
|
231
|
-
if (this.
|
|
232
|
-
serialized.push(this.
|
|
283
|
+
if (this.nameSecondary) {
|
|
284
|
+
serialized.push(this.nameSecondary.serialize());
|
|
233
285
|
|
|
234
|
-
if (this.
|
|
235
|
-
|
|
286
|
+
if (this.paramsSecondary || this.iconsetIdSecondary) {
|
|
287
|
+
const options = this.serializeOptions(this.paramsSecondary, this.iconsetIdSecondary);
|
|
288
|
+
if (options) serialized.push(options);
|
|
236
289
|
}
|
|
237
290
|
}
|
|
238
291
|
|
|
@@ -19,6 +19,7 @@ import Literal from './literal';
|
|
|
19
19
|
import Assertion from './assertion';
|
|
20
20
|
import Coercion from './coercion';
|
|
21
21
|
import At from './at';
|
|
22
|
+
import AtInterpolated from './at_interpolated';
|
|
22
23
|
import In from './in';
|
|
23
24
|
import IndexOf from './index_of';
|
|
24
25
|
import Match from './match';
|
|
@@ -60,6 +61,7 @@ const expressions: ExpressionRegistry = {
|
|
|
60
61
|
'<=': LessThanOrEqual,
|
|
61
62
|
'array': Assertion,
|
|
62
63
|
'at': At,
|
|
64
|
+
'at-interpolated': AtInterpolated,
|
|
63
65
|
'boolean': Assertion,
|
|
64
66
|
'case': Case,
|
|
65
67
|
'coalesce': Coalesce,
|
|
@@ -98,8 +100,7 @@ function rgba(ctx: EvaluationContext, [r, g, b, a]: Expression[]) {
|
|
|
98
100
|
const alpha = a ? a.evaluate(ctx) : 1;
|
|
99
101
|
const error = validateRGBA(r, g, b, alpha);
|
|
100
102
|
if (error) throw new RuntimeError(error);
|
|
101
|
-
|
|
102
|
-
return new Color(r / 255 * alpha, g / 255 * alpha, b / 255 * alpha, alpha);
|
|
103
|
+
return new Color(r as unknown as number / 255 * alpha, g as unknown as number / 255 * alpha, b as unknown as number / 255 * alpha, alpha);
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
function hsla(ctx: EvaluationContext, [h, s, l, a]: Expression[]) {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import UnitBezier from '@mapbox/unitbezier';
|
|
2
2
|
import * as interpolate from '../../util/interpolate';
|
|
3
|
-
import {toString, NumberType, ColorType
|
|
3
|
+
import {toString, NumberType, ColorType} from '../types';
|
|
4
4
|
import {findStopLessThanOrEqualTo} from '../stops';
|
|
5
5
|
import {hcl, lab} from '../../util/color_spaces';
|
|
6
|
-
import Literal from './literal';
|
|
7
|
-
import RuntimeError from '../runtime_error';
|
|
8
6
|
|
|
9
7
|
import type Color from '../../util/color';
|
|
10
8
|
import type {Stops} from '../stops';
|
|
@@ -13,32 +11,30 @@ import type ParsingContext from '../parsing_context';
|
|
|
13
11
|
import type EvaluationContext from '../evaluation_context';
|
|
14
12
|
import type {Type} from '../types';
|
|
15
13
|
|
|
16
|
-
export type InterpolationType =
|
|
17
|
-
name: 'linear'
|
|
18
|
-
|
|
19
|
-
name: '
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
export type InterpolationType =
|
|
15
|
+
| {name: 'linear'}
|
|
16
|
+
| {name: 'exponential'; base: number}
|
|
17
|
+
| {name: 'cubic-bezier'; controlPoints: [number, number, number, number]};
|
|
18
|
+
|
|
19
|
+
export type InterpolationOperator =
|
|
20
|
+
| 'interpolate'
|
|
21
|
+
| 'interpolate-hcl'
|
|
22
|
+
| 'interpolate-lab';
|
|
25
23
|
|
|
26
24
|
class Interpolate implements Expression {
|
|
27
25
|
type: Type;
|
|
28
26
|
|
|
29
|
-
operator:
|
|
27
|
+
operator: InterpolationOperator;
|
|
30
28
|
interpolation: InterpolationType;
|
|
31
29
|
input: Expression;
|
|
32
|
-
dynamicStops: Expression | null;
|
|
33
30
|
labels: Array<number>;
|
|
34
31
|
outputs: Array<Expression>;
|
|
35
32
|
|
|
36
|
-
constructor(type: Type, operator:
|
|
33
|
+
constructor(type: Type, operator: InterpolationOperator, interpolation: InterpolationType, input: Expression, stops: Stops) {
|
|
37
34
|
this.type = type;
|
|
38
35
|
this.operator = operator;
|
|
39
36
|
this.interpolation = interpolation;
|
|
40
37
|
this.input = input;
|
|
41
|
-
this.dynamicStops = dynamicStops;
|
|
42
38
|
|
|
43
39
|
this.labels = [];
|
|
44
40
|
this.outputs = [];
|
|
@@ -67,11 +63,10 @@ class Interpolate implements Expression {
|
|
|
67
63
|
return t;
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Interpolate | null |
|
|
66
|
+
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Interpolate | null | void {
|
|
71
67
|
let [operator, interpolation, input, ...rest] = args;
|
|
72
68
|
|
|
73
69
|
if (!Array.isArray(interpolation) || interpolation.length === 0) {
|
|
74
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
75
70
|
return context.error(`Expected an interpolation type expression.`, 1);
|
|
76
71
|
}
|
|
77
72
|
|
|
@@ -80,7 +75,6 @@ class Interpolate implements Expression {
|
|
|
80
75
|
} else if (interpolation[0] === 'exponential') {
|
|
81
76
|
const base = interpolation[1];
|
|
82
77
|
if (typeof base !== 'number')
|
|
83
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
84
78
|
return context.error(`Exponential interpolation requires a numeric base.`, 1, 1);
|
|
85
79
|
interpolation = {
|
|
86
80
|
name: 'exponential',
|
|
@@ -92,7 +86,6 @@ class Interpolate implements Expression {
|
|
|
92
86
|
controlPoints.length !== 4 ||
|
|
93
87
|
controlPoints.some(t => typeof t !== 'number' || t < 0 || t > 1)
|
|
94
88
|
) {
|
|
95
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
96
89
|
return context.error('Cubic bezier interpolation requires four numeric arguments with values between 0 and 1.', 1);
|
|
97
90
|
}
|
|
98
91
|
|
|
@@ -101,17 +94,14 @@ class Interpolate implements Expression {
|
|
|
101
94
|
controlPoints: (controlPoints as any)
|
|
102
95
|
};
|
|
103
96
|
} else {
|
|
104
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
105
97
|
return context.error(`Unknown interpolation type ${String(interpolation[0])}`, 1, 0);
|
|
106
98
|
}
|
|
107
99
|
|
|
108
|
-
if (args.length - 1 <
|
|
109
|
-
|
|
110
|
-
return context.error(`Expected at least 3 arguments, but found only ${args.length - 1}.`);
|
|
100
|
+
if (args.length - 1 < 4) {
|
|
101
|
+
return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
|
|
111
102
|
}
|
|
112
103
|
|
|
113
104
|
if (args.length - 1 > 3 && (args.length - 1) % 2 !== 0) {
|
|
114
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
115
105
|
return context.error(`Expected an even number of arguments.`);
|
|
116
106
|
}
|
|
117
107
|
|
|
@@ -127,15 +117,6 @@ class Interpolate implements Expression {
|
|
|
127
117
|
outputType = context.expectedType;
|
|
128
118
|
}
|
|
129
119
|
|
|
130
|
-
// Exactly 3 arguments means that the steps are created by an expression
|
|
131
|
-
if (args.length - 1 === 3) {
|
|
132
|
-
const dynamicStops = context.parse(rest[0], 3, ValueType);
|
|
133
|
-
if (!dynamicStops) return null;
|
|
134
|
-
|
|
135
|
-
// @ts-expect-error - TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'InterpolationType'.
|
|
136
|
-
return new Interpolate(outputType, (operator as any), interpolation, input, dynamicStops, stops);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
120
|
for (let i = 0; i < rest.length; i += 2) {
|
|
140
121
|
const label = rest[i];
|
|
141
122
|
const value = rest[i + 1];
|
|
@@ -144,12 +125,10 @@ class Interpolate implements Expression {
|
|
|
144
125
|
const valueKey = i + 4;
|
|
145
126
|
|
|
146
127
|
if (typeof label !== 'number') {
|
|
147
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
148
128
|
return context.error('Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.', labelKey);
|
|
149
129
|
}
|
|
150
130
|
|
|
151
131
|
if (stops.length && stops[stops.length - 1][0] >= label) {
|
|
152
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
153
132
|
return context.error('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.', labelKey);
|
|
154
133
|
}
|
|
155
134
|
|
|
@@ -167,41 +146,15 @@ class Interpolate implements Expression {
|
|
|
167
146
|
typeof outputType.N === 'number'
|
|
168
147
|
)
|
|
169
148
|
) {
|
|
170
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Interpolate'.
|
|
171
149
|
return context.error(`Type ${toString(outputType)} is not interpolatable.`);
|
|
172
150
|
}
|
|
173
151
|
|
|
174
|
-
|
|
175
|
-
return new Interpolate(outputType, (operator as any), interpolation, input, null, stops);
|
|
152
|
+
return new Interpolate(outputType, operator as InterpolationOperator, interpolation as InterpolationType, input as Expression, stops);
|
|
176
153
|
}
|
|
177
154
|
|
|
178
155
|
evaluate(ctx: EvaluationContext): Color {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (this.dynamicStops) {
|
|
183
|
-
const dynamicStopsValue = (this.dynamicStops.evaluate(ctx) as [number]);
|
|
184
|
-
if (dynamicStopsValue.length % 2 !== 0) {
|
|
185
|
-
throw new RuntimeError('Expected an even number of arguments.');
|
|
186
|
-
}
|
|
187
|
-
labels = [];
|
|
188
|
-
outputs = [];
|
|
189
|
-
for (let i = 0; i < dynamicStopsValue.length; i += 2) {
|
|
190
|
-
const label = dynamicStopsValue[i];
|
|
191
|
-
const output = new Literal(NumberType, dynamicStopsValue[i + 1]);
|
|
192
|
-
if (typeof label !== 'number') {
|
|
193
|
-
throw new RuntimeError('Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values.');
|
|
194
|
-
}
|
|
195
|
-
if (labels.length && labels[labels.length - 1] >= label) {
|
|
196
|
-
throw new RuntimeError('Input/output pairs for "interpolate" expressions must be arranged with input values in strictly ascending order.');
|
|
197
|
-
}
|
|
198
|
-
labels.push(label);
|
|
199
|
-
outputs.push(output);
|
|
200
|
-
}
|
|
201
|
-
if (labels.length === 0) {
|
|
202
|
-
throw new RuntimeError('Expected at least one input/output pair.');
|
|
203
|
-
}
|
|
204
|
-
}
|
|
156
|
+
const labels = this.labels;
|
|
157
|
+
const outputs = this.outputs;
|
|
205
158
|
|
|
206
159
|
if (labels.length === 1) {
|
|
207
160
|
return outputs[0].evaluate(ctx);
|
|
@@ -246,31 +199,26 @@ class Interpolate implements Expression {
|
|
|
246
199
|
}
|
|
247
200
|
|
|
248
201
|
serialize(): SerializedExpression {
|
|
249
|
-
let interpolation;
|
|
202
|
+
let interpolation: [InterpolationType['name'], ...number[]];
|
|
250
203
|
if (this.interpolation.name === 'linear') {
|
|
251
|
-
interpolation = [
|
|
204
|
+
interpolation = ['linear'];
|
|
252
205
|
} else if (this.interpolation.name === 'exponential') {
|
|
253
206
|
if (this.interpolation.base === 1) {
|
|
254
|
-
interpolation = [
|
|
207
|
+
interpolation = ['linear'];
|
|
255
208
|
} else {
|
|
256
|
-
interpolation = [
|
|
209
|
+
interpolation = ['exponential', this.interpolation.base];
|
|
257
210
|
}
|
|
258
211
|
} else {
|
|
259
|
-
|
|
260
|
-
interpolation = ["cubic-bezier" ].concat(this.interpolation.controlPoints);
|
|
212
|
+
interpolation = ['cubic-bezier', ...this.interpolation.controlPoints];
|
|
261
213
|
}
|
|
262
214
|
|
|
263
215
|
const serialized = [this.operator, interpolation, this.input.serialize()];
|
|
264
216
|
|
|
265
|
-
|
|
266
|
-
serialized.push(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.labels[i],
|
|
271
|
-
this.outputs[i].serialize()
|
|
272
|
-
);
|
|
273
|
-
}
|
|
217
|
+
for (let i = 0; i < this.labels.length; i++) {
|
|
218
|
+
serialized.push(
|
|
219
|
+
this.labels[i],
|
|
220
|
+
this.outputs[i].serialize()
|
|
221
|
+
);
|
|
274
222
|
}
|
|
275
223
|
return serialized;
|
|
276
224
|
}
|
|
@@ -25,9 +25,8 @@ class Let implements Expression {
|
|
|
25
25
|
fn(this.result);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Let | null |
|
|
28
|
+
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Let | null | void {
|
|
29
29
|
if (args.length < 4)
|
|
30
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Let'.
|
|
31
30
|
return context.error(`Expected at least 3 arguments, but found ${args.length - 1} instead.`);
|
|
32
31
|
|
|
33
32
|
const bindings: Array<[string, Expression]> = [];
|
|
@@ -35,12 +34,10 @@ class Let implements Expression {
|
|
|
35
34
|
const name = args[i];
|
|
36
35
|
|
|
37
36
|
if (typeof name !== 'string') {
|
|
38
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Let'.
|
|
39
37
|
return context.error(`Expected string, but found ${typeof name} instead.`, i);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
if (/[^a-zA-Z0-9_]/.test(name)) {
|
|
43
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Let'.
|
|
44
41
|
return context.error(`Variable names must contain only alphanumeric characters or '_'.`, i);
|
|
45
42
|
}
|
|
46
43
|
|
|
@@ -61,12 +58,10 @@ class Let implements Expression {
|
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
serialize(): SerializedExpression {
|
|
64
|
-
const serialized = ["let"];
|
|
61
|
+
const serialized: SerializedExpression[] = ["let"];
|
|
65
62
|
for (const [name, expr] of this.bindings) {
|
|
66
|
-
// @ts-expect-error - TS2345 - Argument of type 'SerializedExpression' is not assignable to parameter of type 'string'.
|
|
67
63
|
serialized.push(name, expr.serialize());
|
|
68
64
|
}
|
|
69
|
-
// @ts-expect-error - TS2345 - Argument of type 'SerializedExpression' is not assignable to parameter of type 'string'.
|
|
70
65
|
serialized.push(this.result.serialize());
|
|
71
66
|
return serialized;
|
|
72
67
|
}
|
|
@@ -58,8 +58,7 @@ class Literal implements Expression {
|
|
|
58
58
|
// Constant-folding can generate Literal expressions that you
|
|
59
59
|
// couldn't actually generate with a "literal" expression,
|
|
60
60
|
// so we have to implement an equivalent serialization here
|
|
61
|
-
|
|
62
|
-
return ["rgba"].concat(this.value.toRenderColor(null).toArray());
|
|
61
|
+
return ["rgba" as SerializedExpression].concat(this.value.toRenderColor(null).toArray());
|
|
63
62
|
} else if (this.value instanceof Formatted) {
|
|
64
63
|
// Same as Color
|
|
65
64
|
return this.value.serialize();
|
|
@@ -28,12 +28,10 @@ class Match implements Expression {
|
|
|
28
28
|
this.otherwise = otherwise;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Match | null |
|
|
31
|
+
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Match | null | void {
|
|
32
32
|
if (args.length < 5)
|
|
33
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
34
33
|
return context.error(`Expected at least 4 arguments, but found only ${args.length - 1}.`);
|
|
35
34
|
if (args.length % 2 !== 1)
|
|
36
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
37
35
|
return context.error(`Expected an even number of arguments.`);
|
|
38
36
|
|
|
39
37
|
let inputType;
|
|
@@ -52,23 +50,17 @@ class Match implements Expression {
|
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
const labelContext = context.concat(i);
|
|
55
|
-
|
|
56
|
-
if (labels.length === 0) {
|
|
57
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
53
|
+
if ((labels as unknown[]).length === 0) {
|
|
58
54
|
return labelContext.error('Expected at least one branch label.');
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
for (const label of labels) {
|
|
57
|
+
for (const label of (labels as unknown[])) {
|
|
63
58
|
if (typeof label !== 'number' && typeof label !== 'string') {
|
|
64
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
65
59
|
return labelContext.error(`Branch labels must be numbers or strings.`);
|
|
66
60
|
} else if (typeof label === 'number' && Math.abs(label) > Number.MAX_SAFE_INTEGER) {
|
|
67
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
68
61
|
return labelContext.error(`Branch labels must be integers no larger than ${Number.MAX_SAFE_INTEGER}.`);
|
|
69
62
|
|
|
70
63
|
} else if (typeof label === 'number' && Math.floor(label) !== label) {
|
|
71
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
72
64
|
return labelContext.error(`Numeric branch labels must be integer values.`);
|
|
73
65
|
|
|
74
66
|
} else if (!inputType) {
|
|
@@ -78,7 +70,6 @@ class Match implements Expression {
|
|
|
78
70
|
}
|
|
79
71
|
|
|
80
72
|
if (typeof cases[String(label)] !== 'undefined') {
|
|
81
|
-
// @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Match'.
|
|
82
73
|
return labelContext.error('Branch labels must be unique.');
|
|
83
74
|
}
|
|
84
75
|
|