Skip to content

Commit

Permalink
Generate flow declarations from style specification
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Jul 7, 2017
1 parent cf852c1 commit 783a814
Show file tree
Hide file tree
Showing 2 changed files with 402 additions and 0 deletions.
112 changes: 112 additions & 0 deletions build/generate-flow-typed-style-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const spec = require('../src/style-spec/reference/v8.json');
const fs = require('fs');

function flowEnum(values) {
if (Array.isArray(values)) {
return values.map(JSON.stringify).join(' | ');
} else {
return Object.keys(values).map(JSON.stringify).join(' | ');
}
}

function flowType(property) {
if (typeof property.type === 'function') {
return property.type();
}

switch (property.type) {
case 'string':
case 'number':
case 'boolean':
return property.type;
case 'enum':
return flowEnum(property.values);
case 'array':
return `Array<${flowType(typeof property.value === 'string' ? {type: property.value} : property.value)}>`;
case 'light':
return 'LightSpecification';
case 'sources':
return '{[string]: SourceSpecification}';
case '*':
return 'mixed';
default:
return `${property.type.slice(0, 1).toUpperCase()}${property.type.slice(1)}Specification`;
}
}

function flowProperty(key, property) {
return `"${key}"${property.required ? '' : '?'}: ${flowType(property)}`;
}

function flowObjectDeclaration(key, properties) {
return `declare type ${key} = ${flowObject(properties, '', '*' in properties ? '' : '|')}`;
}

function flowObject(properties, indent, sealing = '') {
return `{${sealing}
${Object.keys(properties)
.filter(k => k !== '*')
.map(k => ` ${indent}${flowProperty(k, properties[k])}`)
.join(',\n')}
${indent}${sealing}}`
}

function flowSourceTypeName(key) {
return key.replace(/source_(.)(.*)/, (_, _1, _2) => `${_1.toUpperCase()}${_2}SourceSpecification`);
}

function flowLayerTypeName(key) {
return key.split('-').map(k => k.replace(/(.)(.*)/, (_, _1, _2) => `${_1.toUpperCase()}${_2}`)).concat('LayerSpecification').join('');
}

function flowLayer(key) {
const layer = spec.layer;

layer.type = {
type: 'enum',
values: [key],
required: true
};

delete layer.ref;
delete layer['paint.*'];

layer.paint.type = () => {
return flowObject(spec[`layout_${key}`], ' ', '|');
};

layer.layout.type = () => {
return flowObject(spec[`paint_${key}`], ' ', '|');
};

if (key === 'background') {
delete layer.source;
delete layer['source-layer'];
delete layer.filter;
}

return flowObjectDeclaration(flowLayerTypeName(key), layer);
}

const layerTypes = Object.keys(spec.layer.type.values);

fs.writeFileSync('flow-typed/style-spec.js', `// Generated code; do not edit. Edit build/generate-flow-typed-style-spec.js instead.
declare type ColorSpecification = string;
declare type FilterSpecification = Array<any>;
${flowObjectDeclaration('StyleSpecification', spec.$root)}
${flowObjectDeclaration('LightSpecification', spec.light)}
${spec.source.map(key => flowObjectDeclaration(flowSourceTypeName(key), spec[key])).join('\n\n')}
declare type SourceSpecification =
${spec.source.map(key => ` | ${flowSourceTypeName(key)}`).join('\n')}
${layerTypes.map(key => flowLayer(key)).join('\n\n')}
declare type LayerSpecification =
${layerTypes.map(key => ` | ${flowLayerTypeName(key)}`).join('\n')};
`);
Loading

0 comments on commit 783a814

Please sign in to comment.