1 | import * as fs from 'fs';
|
2 | import * as properties from '../src/style-spec/util/properties';
|
3 |
|
4 | import spec from '../src/style-spec/reference/v8.json';
|
5 |
|
6 | function unionType(values) {
|
7 | if (Array.isArray(values)) {
|
8 | return values.map(v => JSON.stringify(v)).join(' | ');
|
9 | } else {
|
10 | return Object.keys(values).map(v => JSON.stringify(v)).join(' | ');
|
11 | }
|
12 | }
|
13 |
|
14 | function propertyType(property) {
|
15 | if (typeof property.type === 'function') {
|
16 | return property.type();
|
17 | }
|
18 |
|
19 | const baseType = (() => {
|
20 | switch (property.type) {
|
21 | case 'string':
|
22 | case 'number':
|
23 | case 'boolean':
|
24 | return property.type;
|
25 | case 'enum':
|
26 | return unionType(property.values);
|
27 | case 'array': {
|
28 | const elementType = propertyType(typeof property.value === 'string' ? {type: property.value, values: property.values} : property.value);
|
29 | if (property.length) {
|
30 | return `[${Array(property.length).fill(elementType).join(', ')}]`;
|
31 | } else {
|
32 | return `Array<${elementType}>`;
|
33 | }
|
34 | }
|
35 | case 'light':
|
36 | return 'LightSpecification';
|
37 | case 'sources':
|
38 | return '{[_: string]: SourceSpecification}';
|
39 | case '*':
|
40 | return 'unknown';
|
41 | default:
|
42 | return `${property.type.slice(0, 1).toUpperCase()}${property.type.slice(1)}Specification`;
|
43 | }
|
44 | })();
|
45 |
|
46 | if (properties.supportsPropertyExpression(property)) {
|
47 | return `DataDrivenPropertyValueSpecification<${baseType}>`;
|
48 | } else if (properties.supportsZoomExpression(property)) {
|
49 | return `PropertyValueSpecification<${baseType}>`;
|
50 | } else if (property.expression) {
|
51 | return 'ExpressionSpecificationArray';
|
52 | } else {
|
53 | return baseType;
|
54 | }
|
55 | }
|
56 |
|
57 | function propertyDeclaration(key, property) {
|
58 | return `"${key}"${property.required ? '' : '?'}: ${propertyType(property)}`;
|
59 | }
|
60 |
|
61 | function objectDeclaration(key, properties) {
|
62 | return `export type ${key} = ${objectType(properties, '')};`;
|
63 | }
|
64 |
|
65 | function objectType(properties, indent) {
|
66 | return `{
|
67 | ${Object.keys(properties)
|
68 | .filter(k => k !== '*')
|
69 | .map(k => ` ${indent}${propertyDeclaration(k, properties[k])}`)
|
70 | .join(',\n')}
|
71 | ${indent}}`;
|
72 | }
|
73 |
|
74 | function sourceTypeName(key) {
|
75 | return key.replace(/source_(.)(.*)/, (_, _1, _2) => `${_1.toUpperCase()}${_2}SourceSpecification`)
|
76 | .replace(/_dem/, 'DEM')
|
77 | .replace(/Geojson/, 'GeoJSON');
|
78 | }
|
79 |
|
80 | function layerTypeName(key) {
|
81 | return key.split('-').map(k => k.replace(/(.)(.*)/, (_, _1, _2) => `${_1.toUpperCase()}${_2}`)).concat('LayerSpecification').join('');
|
82 | }
|
83 |
|
84 | function layerType(key) {
|
85 | const layer = spec.layer as any;
|
86 |
|
87 | layer.type = {
|
88 | type: 'enum',
|
89 | values: [key],
|
90 | required: true
|
91 | };
|
92 |
|
93 | delete layer.ref;
|
94 | delete layer['paint.*'];
|
95 |
|
96 | layer.paint.type = () => {
|
97 | return objectType(spec[`paint_${key}`], ' ');
|
98 | };
|
99 |
|
100 | layer.layout.type = () => {
|
101 | return objectType(spec[`layout_${key}`], ' ');
|
102 | };
|
103 |
|
104 | if (key === 'background') {
|
105 | delete layer.source;
|
106 | delete layer['source-layer'];
|
107 | delete layer.filter;
|
108 | } else {
|
109 | layer.source.required = true;
|
110 | }
|
111 |
|
112 | return objectDeclaration(layerTypeName(key), layer);
|
113 | }
|
114 |
|
115 | const layerTypes = Object.keys(spec.layer.type.values);
|
116 |
|
117 | fs.writeFileSync('src/style-spec/types.g.ts',
|
118 | `// Generated code; do not edit. Edit build/generate-style-spec.ts instead.
|
119 | /* eslint-disable */
|
120 |
|
121 | export type ColorSpecification = string;
|
122 |
|
123 | export type FormattedSpecification = string;
|
124 |
|
125 | export type ResolvedImageSpecification = string;
|
126 |
|
127 | export type PromoteIdSpecification = {[_: string]: string} | string;
|
128 |
|
129 | export type FilterSpecificationInputType = string | number | boolean;
|
130 | export type FilterSpecification =
|
131 | // Lookup
|
132 | | ['at', number, (number |string)[]]
|
133 | | ['get', string, Record<string, unknown>?]
|
134 | | ['has', string, Record<string, unknown>?]
|
135 | | ['in', ...FilterSpecificationInputType[], FilterSpecificationInputType | FilterSpecificationInputType[]]
|
136 | | ['index-of', FilterSpecificationInputType, FilterSpecificationInputType | FilterSpecificationInputType[]]
|
137 | | ['length', string | string[]]
|
138 | | ['slice', string | string[], number]
|
139 | // Decision
|
140 | | ['!', FilterSpecification]
|
141 | | ['!=', string | FilterSpecification, FilterSpecificationInputType]
|
142 | | ['<', string | FilterSpecification, FilterSpecificationInputType]
|
143 | | ['<=', string | FilterSpecification, FilterSpecificationInputType]
|
144 | | ['==', string | FilterSpecification, FilterSpecificationInputType]
|
145 | | ['>', string | FilterSpecification, FilterSpecificationInputType]
|
146 | | ['>=', string | FilterSpecification, FilterSpecificationInputType]
|
147 | | ["all", ...FilterSpecification[], FilterSpecificationInputType]
|
148 | | ["any", ...FilterSpecification[], FilterSpecificationInputType]
|
149 | | ["case", ...FilterSpecification[], FilterSpecificationInputType]
|
150 | | ["coalesce", ...FilterSpecification[], FilterSpecificationInputType]
|
151 | | ["match", ...FilterSpecification[], FilterSpecificationInputType]
|
152 | | ["within", ...FilterSpecification[], FilterSpecificationInputType]
|
153 | // Used in convert.ts
|
154 | | ["!in", ...FilterSpecification[], FilterSpecificationInputType]
|
155 | | ["!has", ...FilterSpecification[], FilterSpecificationInputType]
|
156 | | ["none", ...FilterSpecification[], FilterSpecificationInputType]
|
157 | // Fallbak for others
|
158 | | Array<string | FilterSpecification>
|
159 |
|
160 | export type TransitionSpecification = {
|
161 | duration?: number,
|
162 | delay?: number
|
163 | };
|
164 |
|
165 | // Note: doesn't capture interpolatable vs. non-interpolatable types.
|
166 |
|
167 | export type CameraFunctionSpecification<T> =
|
168 | { type: 'exponential', stops: Array<[number, T]> }
|
169 | | { type: 'interval', stops: Array<[number, T]> };
|
170 |
|
171 | export type SourceFunctionSpecification<T> =
|
172 | { type: 'exponential', stops: Array<[number, T]>, property: string, default?: T }
|
173 | | { type: 'interval', stops: Array<[number, T]>, property: string, default?: T }
|
174 | | { type: 'categorical', stops: Array<[string | number | boolean, T]>, property: string, default?: T }
|
175 | | { type: 'identity', property: string, default?: T };
|
176 |
|
177 | export type CompositeFunctionSpecification<T> =
|
178 | { type: 'exponential', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T }
|
179 | | { type: 'interval', stops: Array<[{zoom: number, value: number}, T]>, property: string, default?: T }
|
180 | | { type: 'categorical', stops: Array<[{zoom: number, value: string | number | boolean}, T]>, property: string, default?: T };
|
181 |
|
182 | export type ExpressionSpecificationArray = Array<unknown>;
|
183 |
|
184 | export type PropertyValueSpecification<T> =
|
185 | T
|
186 | | CameraFunctionSpecification<T>
|
187 | | ExpressionSpecificationArray;
|
188 |
|
189 | export type DataDrivenPropertyValueSpecification<T> =
|
190 | T
|
191 | | CameraFunctionSpecification<T>
|
192 | | SourceFunctionSpecification<T>
|
193 | | CompositeFunctionSpecification<T>
|
194 | | ExpressionSpecificationArray;
|
195 |
|
196 | ${objectDeclaration('StyleSpecification', spec.$root)}
|
197 |
|
198 | ${objectDeclaration('LightSpecification', spec.light)}
|
199 |
|
200 | ${spec.source.map(key => objectDeclaration(sourceTypeName(key), spec[key])).join('\n\n')}
|
201 |
|
202 | export type SourceSpecification =
|
203 | ${spec.source.map(key => ` | ${sourceTypeName(key)}`).join('\n')}
|
204 |
|
205 | ${layerTypes.map(key => layerType(key)).join('\n\n')}
|
206 |
|
207 | export type LayerSpecification =
|
208 | ${layerTypes.map(key => ` | ${layerTypeName(key)}`).join('\n')};
|
209 |
|
210 | `);
|
211 |
|
\ | No newline at end of file |