From 64258a1ac4209bf1b8aa60d1ef193310268fe58e Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 26 Jan 2018 00:10:46 +0100 Subject: [PATCH] Add generate-ol task to create a full build Creating the full build will also serve as type checker. --- .gitignore | 1 + tasks/generate-ol.js | 170 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 tasks/generate-ol.js diff --git a/.gitignore b/.gitignore index c38cba70da..2f836a692e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /coverage/ /dist/ /node_modules/ +src/ol.js diff --git a/tasks/generate-ol.js b/tasks/generate-ol.js new file mode 100644 index 0000000000..df24bdddfc --- /dev/null +++ b/tasks/generate-ol.js @@ -0,0 +1,170 @@ +const fs = require('fs-extra'); +const path = require('path'); +const async = require('async'); +const generateInfo = require('./generate-info'); + + +/** + * Read the symbols from info file. + * @param {function(Error, Array., Array.)} callback Called + * with the patterns and symbols (or any error). + */ +function getSymbols(callback) { + generateInfo(function(err) { + if (err) { + callback(new Error('Trouble generating info: ' + err.message)); + return; + } + const symbols = require('../build/info.json').symbols; + callback(null, symbols.filter(symbol => symbol.kind != 'member')); + }); +} + + +/** + * Generate a list of symbol names. + * + * @param {Array.} symbols List of symbols. + * @param {function(Error, Array., Array.)} callback Called with + * the filtered list of symbols and a list of all provides (or any error). + */ +function addImports(symbols, callback) { + const imports = {}; + symbols.forEach(function(symbol) { + const defaultExport = symbol.name.split('~'); + const namedExport = symbol.name.split('.'); + if (defaultExport.length > 1) { + const from = defaultExport[0].replace(/^module\:/, './'); + const importName = from.replace(/[.\/]+/g, '$'); + const defaultImport = `import ${importName} from '${from}.js';`; + imports[defaultImport] = true; + } else if (namedExport.length > 1) { + const from = namedExport[0].replace(/^module\:/, './'); + const importName = from.replace(/[.\/]+/g, '_'); + const namedImport = `import * as ${importName} from '${from}.js';`; + imports[namedImport] = true; + } + }); + + callback(null, symbols, Object.keys(imports).sort()); +} + + +/** + * Generate goog code to export a named symbol. + * @param {string} name Symbol name. + * @param {Object.} namespaces Already defined namespaces. + * @return {string} Export code. + */ +function formatSymbolExport(name, namespaces) { + const parts = name.split('~'); + const isNamed = parts[0].indexOf('.') !== -1; + const nsParts = parts[0].replace(/^module\:/, '').split(/[\/\.]/); + const last = nsParts.length - 1; + let importName = isNamed ? + '_' + nsParts.slice(0, last).join('_') + '.' + nsParts[last] : + '$' + nsParts.join('$'); + if (parts.length > 1 && parts[1].indexOf('.') !== -1) { + const property = parts[1].split('.').pop(); + importName += '.' + property; + nsParts.push(property); + } + let line = nsParts[0]; + for (let i = 1, ii = nsParts.length; i < ii; ++i) { + line += `['${nsParts[i]}']`; + namespaces[line] = (line in namespaces ? namespaces[line] : true) && i < ii - 1; + } + line += ` = ${importName};`; + return line; +} + + +/** + * Generate goog code to export a property. + * @param {string} name Property long name (e.g. foo.Bar#baz). + * @return {string} Export code. + */ +function formatPropertyExport(name) { + const parts = name.split('#'); + const prototype = parts[0].split('~')[0].replace(/^module\:/, './').replace(/[.\/]+/g, '$') + '.prototype'; + const property = parts[1]; + return `${prototype}['${property}'] = ${prototype}.${property};`; +} + + +/** + * Generate export code given a list symbol names. + * @param {Array.} symbols List of symbols. + * @param {Object.} namespaces Already defined namespaces. + * @param {Array.} imports List of all imports. + * @return {string} Export code. + */ +function generateExports(symbols, namespaces, imports) { + let blocks = []; + symbols.forEach(function(symbol) { + const name = symbol.name; + if (name.indexOf('#') > 0) { + blocks.push(formatPropertyExport(name)); + } else { + blocks.push(formatSymbolExport(name, namespaces)); + } + }); + const nsdefs = ['const ol = window[\'ol\'] = {};']; + const ns = Object.keys(namespaces).sort(); + for (let i = 0, ii = ns.length; i < ii; ++i) { + if (namespaces[ns[i]]) { + nsdefs.push(`${ns[i]} = {};`); + } + } + blocks = imports.concat(nsdefs).concat(blocks); + blocks.push(''); + return blocks.join('\n'); +} + + +/** + * Generate the exports code. + * + * @param {function(Error, string)} callback Called with the exports code or any + * error generating it. + */ +function main(callback) { + async.waterfall([ + getSymbols, + addImports, + function(symbols, imports, done) { + let code, err; + try { + code = generateExports(symbols, {}, imports); + } catch (e) { + err = e; + } + done(err, code); + } + ], callback); +} + + +/** + * If running this module directly, read the config file, call the main + * function, and write the output file. + */ +if (require.main === module) { + async.waterfall([ + main, + fs.outputFile.bind(fs, path.join('src', 'ol.js')) + ], function(err) { + if (err) { + process.stderr.write(err.message + '\n'); + process.exit(1); + } else { + process.exit(0); + } + }); +} + + +/** + * Export main function. + */ +module.exports = main;