UNPKG

@mapbox/mapbox-gl-style-spec

Version:

a specification for mapbox gl styles

96 lines (82 loc) 3.57 kB
import assert from 'assert'; import {checkSubtype, ValueType} from '../types'; import ResolvedImage from '../types/resolved_image'; import type {Expression, SerializedExpression} from '../expression'; import type ParsingContext from '../parsing_context'; import type EvaluationContext from '../evaluation_context'; import type {Type} from '../types'; class Coalesce implements Expression { type: Type; args: Array<Expression>; constructor(type: Type, args: Array<Expression>) { this.type = type; this.args = args; } static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Coalesce | null | undefined { if (args.length < 2) { // @ts-expect-error - TS2322 - Type 'void' is not assignable to type 'Coalesce'. return context.error("Expectected at least one argument."); } let outputType: Type = null; const expectedType = context.expectedType; if (expectedType && expectedType.kind !== 'value') { outputType = expectedType; } const parsedArgs = []; for (const arg of args.slice(1)) { const parsed = context.parse(arg, 1 + parsedArgs.length, outputType, undefined, {typeAnnotation: 'omit'}); if (!parsed) return null; outputType = outputType || parsed.type; parsedArgs.push(parsed); } assert(outputType); // Above, we parse arguments without inferred type annotation so that // they don't produce a runtime error for `null` input, which would // preempt the desired null-coalescing behavior. // Thus, if any of our arguments would have needed an annotation, we // need to wrap the enclosing coalesce expression with it instead. const needsAnnotation = expectedType && parsedArgs.some(arg => checkSubtype(expectedType, arg.type)); return needsAnnotation ? new Coalesce(ValueType, parsedArgs) : new Coalesce(outputType, parsedArgs); } // eslint-disable-next-line @typescript-eslint/no-explicit-any evaluate(ctx: EvaluationContext): any { let result = null; let argCount = 0; let firstImage; for (const arg of this.args) { argCount++; result = arg.evaluate(ctx); // we need to keep track of the first requested image in a coalesce statement // if coalesce can't find a valid image, we return the first image so styleimagemissing can fire if (result && result instanceof ResolvedImage && !result.available) { // set to first image if (!firstImage) { firstImage = result; } result = null; // if we reach the end, return the first image if (argCount === this.args.length) { return firstImage; } } if (result !== null) break; } return result; } eachChild(fn: (_: Expression) => void) { this.args.forEach(fn); } outputDefined(): boolean { return this.args.every(arg => arg.outputDefined()); } serialize(): SerializedExpression { const serialized = ["coalesce"]; // @ts-expect-error - TS2345 - Argument of type 'SerializedExpression' is not assignable to parameter of type 'string'. this.eachChild(child => { serialized.push(child.serialize()); }); return serialized; } } export default Coalesce;