288 lines
9.0 KiB
TypeScript
288 lines
9.0 KiB
TypeScript
'use strict';
|
|
|
|
import * as fs from 'fs';
|
|
|
|
import {v8} from '@maplibre/maplibre-gl-style-spec';
|
|
|
|
function camelCase(str: string): string {
|
|
return str.replace(/-(.)/g, (_, x) => {
|
|
return x.toUpperCase();
|
|
});
|
|
}
|
|
|
|
function pascalCase(str: string): string {
|
|
const almostCamelized = camelCase(str);
|
|
return almostCamelized[0].toUpperCase() + almostCamelized.slice(1);
|
|
}
|
|
|
|
function nativeType(property) {
|
|
switch (property.type) {
|
|
case 'boolean':
|
|
return 'boolean';
|
|
case 'number':
|
|
return 'number';
|
|
case 'string':
|
|
return 'string';
|
|
case 'enum':
|
|
return Object.keys(property.values).map(v => JSON.stringify(v)).join(' | ');
|
|
case 'color':
|
|
return 'Color';
|
|
case 'padding':
|
|
return 'Padding';
|
|
case 'numberArray':
|
|
return 'NumberArray';
|
|
case 'colorArray':
|
|
return 'ColorArray';
|
|
case 'variableAnchorOffsetCollection':
|
|
return 'VariableAnchorOffsetCollection';
|
|
case 'sprite':
|
|
return 'Sprite';
|
|
case 'formatted':
|
|
return 'Formatted';
|
|
case 'resolvedImage':
|
|
return 'ResolvedImage';
|
|
case 'array':
|
|
if (property.length) {
|
|
return `[${new Array(property.length).fill(nativeType({type: property.value})).join(', ')}]`;
|
|
} else {
|
|
return `Array<${nativeType({type: property.value, values: property.values})}>`;
|
|
}
|
|
default: throw new Error(`unknown type "${property.type}" for "${property.name}"`);
|
|
}
|
|
}
|
|
|
|
function possiblyEvaluatedType(property) {
|
|
const propType = nativeType(property);
|
|
|
|
switch (property['property-type']) {
|
|
case 'color-ramp':
|
|
return 'ColorRampProperty';
|
|
case 'cross-faded':
|
|
return `CrossFaded<${propType}>`;
|
|
case 'cross-faded-data-driven':
|
|
return `PossiblyEvaluatedPropertyValue<CrossFaded<${propType}>>`;
|
|
case 'data-driven':
|
|
return `PossiblyEvaluatedPropertyValue<${propType}>`;
|
|
}
|
|
|
|
return propType;
|
|
}
|
|
|
|
function propertyType(property) {
|
|
switch (property['property-type']) {
|
|
case 'data-driven':
|
|
return `DataDrivenProperty<${nativeType(property)}>`;
|
|
case 'cross-faded':
|
|
return `CrossFadedProperty<${nativeType(property)}>`;
|
|
case 'cross-faded-data-driven':
|
|
return `CrossFadedDataDrivenProperty<${nativeType(property)}>`;
|
|
case 'color-ramp':
|
|
return 'ColorRampProperty';
|
|
case 'data-constant':
|
|
case 'constant':
|
|
return `DataConstantProperty<${nativeType(property)}>`;
|
|
default:
|
|
throw new Error(`unknown property-type "${property['property-type']}" for ${property.name}`);
|
|
}
|
|
}
|
|
|
|
function runtimeType(property) {
|
|
switch (property.type) {
|
|
case 'boolean':
|
|
return 'BooleanType';
|
|
case 'number':
|
|
return 'NumberType';
|
|
case 'string':
|
|
case 'enum':
|
|
return 'StringType';
|
|
case 'color':
|
|
return 'ColorType';
|
|
case 'padding':
|
|
return 'PaddingType';
|
|
case 'variableAnchorOffsetCollection':
|
|
return 'VariableAnchorOffsetCollectionType';
|
|
case 'sprite':
|
|
return 'SpriteType';
|
|
case 'formatted':
|
|
return 'FormattedType';
|
|
case 'Image':
|
|
return 'ImageType';
|
|
case 'array':
|
|
if (property.length) {
|
|
return `array(${runtimeType({type: property.value})}, ${property.length})`;
|
|
} else {
|
|
return `array(${runtimeType({type: property.value})})`;
|
|
}
|
|
default: throw new Error(`unknown type "${property.type}" for "${property.name}"`);
|
|
}
|
|
}
|
|
|
|
function overrides(property) {
|
|
return `{ runtimeType: ${runtimeType(property)}, getOverride: (o) => o.${camelCase(property.name)}, hasOverride: (o) => !!o.${camelCase(property.name)} }`;
|
|
}
|
|
|
|
function propertyValue(property, type) {
|
|
const propertyAsSpec = `styleSpec["${type}_${property.layerType}"]["${property.name}"] as any as StylePropertySpecification`;
|
|
|
|
switch (property['property-type']) {
|
|
case 'data-driven':
|
|
if (property.overridable) {
|
|
return `new DataDrivenProperty(${propertyAsSpec}, ${overrides(property)})`;
|
|
} else {
|
|
return `new DataDrivenProperty(${propertyAsSpec})`;
|
|
}
|
|
case 'cross-faded':
|
|
return `new CrossFadedProperty(${propertyAsSpec})`;
|
|
case 'cross-faded-data-driven':
|
|
return `new CrossFadedDataDrivenProperty(${propertyAsSpec})`;
|
|
case 'color-ramp':
|
|
return `new ColorRampProperty(${propertyAsSpec})`;
|
|
case 'data-constant':
|
|
case 'constant':
|
|
return `new DataConstantProperty(${propertyAsSpec})`;
|
|
default:
|
|
throw new Error(`unknown property-type "${property['property-type']}" for ${property.name}`);
|
|
}
|
|
}
|
|
|
|
const layers = Object.keys(v8.layer.type.values).map((type) => {
|
|
const layoutProperties = Object.keys(v8[`layout_${type}`]).reduce((memo, name) => {
|
|
if (name !== 'visibility') {
|
|
v8[`layout_${type}`][name].name = name;
|
|
v8[`layout_${type}`][name].layerType = type;
|
|
memo.push(v8[`layout_${type}`][name]);
|
|
}
|
|
return memo;
|
|
}, []);
|
|
|
|
const paintProperties = Object.keys(v8[`paint_${type}`]).reduce((memo, name) => {
|
|
v8[`paint_${type}`][name].name = name;
|
|
v8[`paint_${type}`][name].layerType = type;
|
|
memo.push(v8[`paint_${type}`][name]);
|
|
return memo;
|
|
}, []);
|
|
|
|
return {type, layoutProperties, paintProperties};
|
|
});
|
|
|
|
function emitlayerProperties(locals) {
|
|
const output = [];
|
|
const layerType = pascalCase(locals.type);
|
|
const {
|
|
layoutProperties,
|
|
paintProperties
|
|
} = locals;
|
|
|
|
output.push(
|
|
`// This file is generated. Edit build/generate-style-code.ts, then run 'npm run codegen'.
|
|
/* eslint-disable */
|
|
|
|
import {latest as styleSpec} from '@maplibre/maplibre-gl-style-spec';
|
|
|
|
import {
|
|
Properties,
|
|
DataConstantProperty,
|
|
DataDrivenProperty,
|
|
CrossFadedDataDrivenProperty,
|
|
CrossFadedProperty,
|
|
ColorRampProperty,
|
|
PossiblyEvaluatedPropertyValue,
|
|
CrossFaded
|
|
} from '../properties';
|
|
|
|
import type {Color, Formatted, Padding, NumberArray, ColorArray, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
`);
|
|
|
|
const overridables = paintProperties.filter(p => p.overridable);
|
|
if (overridables.length) {
|
|
const overridesArray = `import {
|
|
${overridables.reduce((imports, prop) => { imports.push(runtimeType(prop)); return imports; }, []).join(',\n ')}
|
|
} from '@maplibre/maplibre-gl-style-spec';
|
|
`;
|
|
output.push(overridesArray);
|
|
}
|
|
|
|
if (layoutProperties.length) {
|
|
output.push(
|
|
`export type ${layerType}LayoutProps = {`);
|
|
|
|
for (const property of layoutProperties) {
|
|
output.push(
|
|
` "${property.name}": ${propertyType(property)},`);
|
|
}
|
|
|
|
output.push(
|
|
`};
|
|
|
|
export type ${layerType}LayoutPropsPossiblyEvaluated = {`);
|
|
|
|
for (const property of layoutProperties) {
|
|
output.push(
|
|
` "${property.name}": ${possiblyEvaluatedType(property)},`);
|
|
}
|
|
|
|
output.push(
|
|
`};
|
|
|
|
let layout: Properties<${layerType}LayoutProps>;
|
|
const getLayout = () => layout = layout || new Properties({`);
|
|
|
|
for (const property of layoutProperties) {
|
|
output.push(
|
|
` "${property.name}": ${propertyValue(property, 'layout')},`);
|
|
}
|
|
|
|
output.push(
|
|
'});');
|
|
}
|
|
|
|
if (paintProperties.length) {
|
|
output.push(
|
|
`
|
|
export type ${layerType}PaintProps = {`);
|
|
|
|
for (const property of paintProperties) {
|
|
output.push(
|
|
` "${property.name}": ${propertyType(property)},`);
|
|
}
|
|
|
|
output.push(
|
|
`};
|
|
|
|
export type ${layerType}PaintPropsPossiblyEvaluated = {`);
|
|
|
|
for (const property of paintProperties) {
|
|
output.push(
|
|
` "${property.name}": ${possiblyEvaluatedType(property)},`);
|
|
}
|
|
|
|
output.push(
|
|
'};');
|
|
} else {
|
|
output.push(
|
|
`export type ${layerType}PaintProps = {};`);
|
|
}
|
|
|
|
output.push(
|
|
`
|
|
let paint: Properties<${layerType}PaintProps>;
|
|
const getPaint = () => paint = paint || new Properties({`);
|
|
|
|
for (const property of paintProperties) {
|
|
output.push(
|
|
` "${property.name}": ${propertyValue(property, 'paint')},`);
|
|
}
|
|
|
|
output.push(
|
|
`});
|
|
|
|
export default ({ get paint() { return getPaint() }${layoutProperties.length ? ', get layout() { return getLayout() }' : ''} });`);
|
|
|
|
return output.join('\n');
|
|
}
|
|
|
|
for (const layer of layers) {
|
|
fs.writeFileSync(`src/style/style_layer/${layer.type.replace('-', '_')}_style_layer_properties.g.ts`, emitlayerProperties(layer));
|
|
}
|