tool/schemaHelper.js (170 lines of code) (raw):

function traverse(schema, rootName, cb) { function innerTraverse(schemaNode, path, noCallback) { if (!noCallback) { cb(path, schemaNode); } if (schemaNode.items) { if (schemaNode.items.anyOf) { schemaNode.items.anyOf.forEach(itemSchema => { let typeValue = itemSchema.properties && itemSchema.properties.type && itemSchema.properties.type.default; if (typeValue) { innerTraverse( itemSchema, path ? (path + '-' + typeValue.replace(/'/g, '')) : typeValue, false ); } }); } else { innerTraverse(schemaNode.items, path, true); } } else if (schemaNode.properties) { for (let propName in schemaNode.properties) { innerTraverse( schemaNode.properties[propName], path ? (path + '.' + propName) : propName, false ); } } // Or it's a leaf node. } innerTraverse(schema.option, rootName, false); }; module.exports.traverse = traverse; function convertToTree(rootSchema, rootNode) { function createNodeBase(schema) { let schemaType = schema.type; // Simplify type if (schemaType instanceof Array && schemaType.length === 1) { schemaType = schemaType[0]; } let nodeBase = {}; // Get type from default if possible. Reduce size. if (schema.default == null || typeof schema.default !== schemaType) { nodeBase.type = schemaType; } if (schema.default != null) { nodeBase.default = schema.default; } if (schema.items) { // Array also may has properties. nodeBase.isArray = true; } else if (schema.properties && Object.keys(schema.properties).length) { nodeBase.isObject = true; } return nodeBase; } function createArrayItemNode(schema, parentNode) { let childNode = createNodeBase(schema, parentNode); if (schema.properties && schema.properties.type && schema.properties.type.default) { childNode.arrayItemType = schema.properties.type.default.replace(/'/g, ''); } else { console.error('schema', schema); throw new Error('Some thing wrong happens'); } return childNode; } function createPropertyNode(propName, schema, parentNode) { let childNode = createNodeBase(schema, parentNode); childNode.prop = propName; return childNode; } function processObjectType(currentSchema, currentNode) { if (!currentSchema.properties) { return; } let children = []; for (let propName in currentSchema.properties) { let childSchema = currentSchema.properties[propName]; let childNode = createPropertyNode(propName, childSchema, currentNode); processRecursively(childSchema, childNode); children.push(childNode); } if (children.length) { currentNode.children = children; } } function processArrayType(currentSchema, currentNode) { if (!currentSchema.items) { return; } // Each item of array may have different type of object. // Like series, visualMap, legend if (currentSchema.items.anyOf) { let children = []; currentSchema.items.anyOf.forEach(itemSchema => { let childNode = createArrayItemNode(itemSchema, currentNode); processRecursively(itemSchema, childNode); children.push(childNode); }); currentNode.children = children; } // Each item of array only have one type of object. // Like data and most of the compoents. else { processObjectType( currentSchema.items, currentNode ); } } function processRecursively(currentSchema, currentNode) { // Array also may has properties. if (currentSchema.items) { processArrayType(currentSchema, currentNode); } else if (currentSchema.properties) { processObjectType(currentSchema, currentNode); } return currentNode; } return processRecursively( rootSchema, rootNode, 0 ); }; module.exports.extractOptionKeys = function (schema) { const keysRepeatCount = {}; const keysList = []; traverse(schema, '', (schemaPath, schemaNode) => { if (!schemaPath) { return; } const leafKey = schemaPath.split(/[\.-]/g).pop(); if (keysRepeatCount[leafKey] == null) { keysRepeatCount[leafKey] = 0; keysList.push(leafKey); } keysRepeatCount[leafKey]++; }); return keysList.map(key => { return { name: key, count: keysRepeatCount[key] }; }).sort((a, b) => b.count - a.count); }; // Partion the descriptions by the first part of path. For example // { "title.label", "series-line.data", "series-bar.data" } // Will be // { // "title": { "label" }, // "series-line": {"data"}, // "series-bar": {"data"} // } module.exports.extractDesc = function (schema, docName) { let descriptionsMap = {}; let propWithUIControlCount = 0; let propTotalCount = 0; traverse(schema, docName, (schemaPath, schemaNode) => { if (schemaNode.description) { // Extract component level path let parts = schemaPath.split('.'); let divider = parts.length > 2 ? 2 : 1; let partionKey = parts.slice(0, divider).join('.'); let subKey = parts.slice(divider).join('.'); descriptionsMap[partionKey] = descriptionsMap[partionKey] || {}; descriptionsMap[partionKey][subKey] = { desc: schemaNode.description, exampleBaseOptions: schemaNode.exampleBaseOptions, uiControl: schemaNode.uiControl }; propTotalCount++; if (schemaNode.uiControl) { propWithUIControlCount++; } } }); console.log(`Options with UIControl ${propWithUIControlCount} / ${propTotalCount} (${propWithUIControlCount/propTotalCount})`); return { outline: convertToTree(schema.option, {}), descriptions: descriptionsMap }; };