From 8e6711835c36693fc266867986a74c8323a1a425 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 31 Jul 2014 16:34:57 +0200 Subject: [PATCH 01/25] Move plugins one level up so they can be used by other configurations --- buildcfg/jsdoc/{info => }/api-plugin.js | 0 buildcfg/jsdoc/{info => }/define-plugin.js | 0 buildcfg/jsdoc/info/conf.json | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename buildcfg/jsdoc/{info => }/api-plugin.js (100%) rename buildcfg/jsdoc/{info => }/define-plugin.js (100%) diff --git a/buildcfg/jsdoc/info/api-plugin.js b/buildcfg/jsdoc/api-plugin.js similarity index 100% rename from buildcfg/jsdoc/info/api-plugin.js rename to buildcfg/jsdoc/api-plugin.js diff --git a/buildcfg/jsdoc/info/define-plugin.js b/buildcfg/jsdoc/define-plugin.js similarity index 100% rename from buildcfg/jsdoc/info/define-plugin.js rename to buildcfg/jsdoc/define-plugin.js diff --git a/buildcfg/jsdoc/info/conf.json b/buildcfg/jsdoc/info/conf.json index c66f3993ab..3d2c6c6e53 100644 --- a/buildcfg/jsdoc/info/conf.json +++ b/buildcfg/jsdoc/info/conf.json @@ -10,7 +10,7 @@ "includePattern": "\\.js$" }, "plugins": [ - "buildcfg/jsdoc/info/api-plugin", - "buildcfg/jsdoc/info/define-plugin" + "buildcfg/jsdoc/api-plugin", + "buildcfg/jsdoc/define-plugin" ] } From 0b622ba5e7b17ffc8065c165b0969cf8880037c2 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 31 Jul 2014 16:35:28 +0200 Subject: [PATCH 02/25] Add JSDoc configuration to generate ol3 externs --- buildcfg/jsdoc/externs/conf.json | 16 ++++ buildcfg/jsdoc/externs/publish.js | 130 ++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 buildcfg/jsdoc/externs/conf.json create mode 100644 buildcfg/jsdoc/externs/publish.js diff --git a/buildcfg/jsdoc/externs/conf.json b/buildcfg/jsdoc/externs/conf.json new file mode 100644 index 0000000000..0371382163 --- /dev/null +++ b/buildcfg/jsdoc/externs/conf.json @@ -0,0 +1,16 @@ +{ + "opts": { + "recurse": true, + "template": "buildcfg/jsdoc/externs" + }, + "tags": { + "allowUnknownTags": true + }, + "source": { + "includePattern": "\\.js$" + }, + "plugins": [ + "buildcfg/jsdoc/api-plugin", + "buildcfg/jsdoc/define-plugin" + ] +} diff --git a/buildcfg/jsdoc/externs/publish.js b/buildcfg/jsdoc/externs/publish.js new file mode 100644 index 0000000000..f050be590c --- /dev/null +++ b/buildcfg/jsdoc/externs/publish.js @@ -0,0 +1,130 @@ +/** + * @fileoverview Generates a closure compiler externs file for exportable + * symbols (those with an api tag) and boolean defines (with a define tag and a + * default value). + */ +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); + + +/** + * Publish hook for the JSDoc template. Writes to stdout. + * @param {function} data The root of the Taffy DB containing doclet records. + * @param {Object} opts Options. + */ +exports.publish = function(data, opts) { + + // get all doclets with the "api" property or define (excluding enums, + // typedefs and events) + var docs = data( + [{define: {isObject: true}}, {api: {isString: true}}], + {isEnum: {'!is': true}}, + {kind: {'!is': 'typedef'}}, + {kind: {'!is': 'event'}}).get(); + + // get symbols data, filter out those that are members of private classes + var output = []; + var namespaces = {}; + var constructors = {}; + docs.filter(function(doc) { + var include = true; + var constructor = doc.memberof; + if (constructor && constructor.substr(-1) === '_') { + assert.strictEqual(doc.inherited, true, + 'Unexpected export on private class: ' + doc.longname); + include = false; + } + return include; + }).forEach(function(doc) { + + var parts = doc.longname.split('#')[0].split('.'); + parts.pop(); + var namespace = []; + parts.forEach(function(part) { + namespace.push(part); + var partialNamespace = namespace.join('.'); + if (!(partialNamespace in namespaces)) { + namespaces[partialNamespace] = true; + output.push('/**'); + output.push(' * @type {Object}'); + output.push(' */'); + output.push( + (namespace.length == 1 ? 'var ' : '') + partialNamespace + ';'); + output.push(''); + } + }); + + var signature = doc.longname; + if (signature.indexOf('#') > 0) { + signature = doc.longname.replace('#', '.prototype.'); + var constructor = doc.longname.split('#')[0]; + if (!(constructor in constructors)) { + constructors[constructor] = true; + output.push('/**'); + output.push(' * @constructor'); + output.push(' */'); + output.push(constructor + ' = function() {}'); + output.push(''); + } + } + + output.push('/**'); + if (doc.define) { + output.push(' * @define'); + output.push(' * @type {boolean}'); + output.push(' */'); + output.push(doc.longname + ';'); + } else { + if (doc.kind == 'class') { + output.push(' * @constructor'); + } + if (doc.type) { + var types = []; + doc.type.names.forEach(function(name) { + types.push(name); + }); + output.push(' * @type {' + types.join('|') + '}'); + } + var args = []; + if (doc.params) { + doc.params.forEach(function(param) { + args.push(param.name); + var names = []; + param.type.names.forEach(function(name) { + names.push(name); + }); + output.push(' * @param {' + + (param.variable ? '...' : '') + + names.join('|') + + (param.optional ? '=' : '') + + '} ' + param.name); + }); + } + if (doc.returns) { + var returnTypes = []; + doc.returns[0].type.names.forEach(function(name) { + returnTypes.push(name); + }); + output.push(' * @return {' + returnTypes.join('|') + '}'); + } + if (doc.tags) { + doc.tags.forEach(function(tag) { + if (tag.title == 'template') { + output.push(' * @' + tag.title + ' ' + tag.value); + } + }); + } + output.push(' */'); + if (doc.kind == 'function' || doc.kind == 'class') { + output.push(signature + ' = function(' + args.join(', ') + ') {};'); + } else { + output.push(signature); + } + } + output.push(''); + }); + + process.stdout.write(output.join('\n')); + +}; From 4cb60feb4045ec8df88314398d0662fb31be1a87 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 31 Jul 2014 16:35:44 +0200 Subject: [PATCH 03/25] Add generate-externs task to create build/ol-externs.js --- tasks/generate-externs.js | 168 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 tasks/generate-externs.js diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js new file mode 100644 index 0000000000..4498990432 --- /dev/null +++ b/tasks/generate-externs.js @@ -0,0 +1,168 @@ +var fs = require('fs'); +var path = require('path'); +var spawn = require('child_process').spawn; + +var async = require('async'); +var fse = require('fs-extra'); +var walk = require('walk').walk; + +var sourceDir = path.join(__dirname, '..', 'src'); +var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); +var externsPath = path.join(__dirname, '..', 'build', 'ol-externs.js'); +var jsdoc = path.join(__dirname, '..', 'node_modules', '.bin', 'jsdoc'); +var jsdocConfig = path.join( + __dirname, '..', 'buildcfg', 'jsdoc', 'externs', 'conf.json'); + + +/** + * Get the mtime of the externs file. + * @param {function(Error, Date)} callback Callback called with any + * error and the mtime of the externs file (zero date if it doesn't exist). + */ +function getExternsTime(callback) { + fs.stat(externsPath, function(err, stats) { + if (err) { + if (err.code === 'ENOENT') { + callback(null, new Date(0)); + } else { + callback(err); + } + } else { + callback(null, stats.mtime); + } + }); +} + + +/** + * Generate a list of all .js paths in the source directory if any are newer + * than the provided date. + * @param {Date} date Modification time of externs file. + * @param {function(Error, Array.)} callback Called with any + * error and the array of source paths (empty if none newer). + */ +function getNewer(date, callback) { + var paths = []; + var newer = false; + + var walker = walk(sourceDir); + walker.on('file', function(root, stats, next) { + var sourcePath = path.join(root, stats.name); + if (/\.js$/.test(sourcePath)) { + paths.push(sourcePath); + if (stats.mtime > date) { + newer = true; + } + } + next(); + }); + walker.on('errors', function() { + callback(new Error('Trouble walking ' + sourceDir)); + }); + walker.on('end', function() { + callback(null, newer ? paths : []); + }); +} + + +/** + * Spawn JSDoc. + * @param {Array.} paths Paths to source files. + * @param {function(Error, string)} callback Callback called with any error and + * the JSDoc output (new metadata). If provided with an empty list of paths + * the callback will be called with null. + */ +function spawnJSDoc(paths, callback) { + if (paths.length === 0) { + process.nextTick(function() { + callback(null, null); + }); + return; + } + + var output = ''; + var errors = ''; + var cwd = path.join(__dirname, '..'); + var child = spawn(jsdoc, ['-c', jsdocConfig].concat(paths), {cwd: cwd}); + + child.stdout.on('data', function(data) { + output += String(data); + }); + + child.stderr.on('data', function(data) { + errors += String(data); + }); + + child.on('exit', function(code) { + if (code) { + callback(new Error(errors || 'JSDoc failed with no output')); + } else { + callback(null, output); + } + }); +} + + +/** + * Write externs file consisting of externs/olx.js and the JSDoc generated + * externs. + * @param {Object} externs JSDoc generated externs. + * @param {function(Error)} callback Callback. + */ +function writeExterns(externs, callback) { + if (externs) { + var olx; + try { + olx = fs.readFileSync(olxPath, {encoding: 'utf-8'}) + .replace(/ \* @api ?(.*)?(\r\n|\n|\r)/gm, ''); + } catch(e) { + process.nextTick(function() { + callback(null); + }); + } + fse.outputFile(externsPath, olx + '\n\n' + externs, callback); + } else { + process.nextTick(function() { + callback(null); + }); + } +} + + +/** + * Determine if source files have been changed, run JSDoc and write updated + * externs if there are any changes. + * + * @param {function(Error)} callback Called when the externs file has been + * written (or an error occurs). + */ +function main(callback) { + async.waterfall([ + getExternsTime, + getNewer, + spawnJSDoc, + writeExterns + ], callback); +} + + +/** + * If running this module directly, read the config file and call the main + * function. + */ +if (require.main === module) { + main(function(err) { + if (err) { + console.error(err.message); + process.exit(1); + } else { + process.exit(0); + } + }); +} + + +/** + * Export main function. + */ +module.exports = main; From 0d86e4a237795899b51030ca81169d8af8f1e2fb Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 31 Jul 2014 17:50:43 +0200 Subject: [PATCH 04/25] Document the generate-externs.js task --- tasks/readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasks/readme.md b/tasks/readme.md index 6cdc5b9031..1e5826242e 100644 --- a/tasks/readme.md +++ b/tasks/readme.md @@ -89,6 +89,13 @@ The `defines` section of `build.json` above lists common settings for the Closur Called internally to generate a `build/exports.js` file optionally with a limited set of exports. +## `generate-externs.js` + +Can be called to generate a Closure externs file for the full OpenLayers 3 API. The externs file will be placed as `ol-externs.js` in the `build/` directory. + +This is useful for projects that use the Closure Compiler to build, but want to use OpenLayers 3 as external library rather than building together with OpenLayers 3. + + ## `generate-info.js` Called internally to parse the library for annotations and write out a `build/info.json` file. From 1e79acac20752b45dec617eedfec9333567b5f1d Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 1 Aug 2014 17:50:59 +0200 Subject: [PATCH 05/25] Build externs file from info.json This change adds all information that is needed to generate the externs file to build/info.json, so tasks/generate-externs.js no longer needs to spawn JSDoc. --- buildcfg/jsdoc/externs/conf.json | 16 -- buildcfg/jsdoc/externs/publish.js | 130 ----------- buildcfg/jsdoc/{ => info}/api-plugin.js | 0 buildcfg/jsdoc/info/conf.json | 4 +- buildcfg/jsdoc/{ => info}/define-plugin.js | 0 buildcfg/jsdoc/info/publish.js | 50 ++++- tasks/generate-externs.js | 249 +++++++++++---------- tasks/readme.md | 5 +- 8 files changed, 180 insertions(+), 274 deletions(-) delete mode 100644 buildcfg/jsdoc/externs/conf.json delete mode 100644 buildcfg/jsdoc/externs/publish.js rename buildcfg/jsdoc/{ => info}/api-plugin.js (100%) rename buildcfg/jsdoc/{ => info}/define-plugin.js (100%) diff --git a/buildcfg/jsdoc/externs/conf.json b/buildcfg/jsdoc/externs/conf.json deleted file mode 100644 index 0371382163..0000000000 --- a/buildcfg/jsdoc/externs/conf.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "opts": { - "recurse": true, - "template": "buildcfg/jsdoc/externs" - }, - "tags": { - "allowUnknownTags": true - }, - "source": { - "includePattern": "\\.js$" - }, - "plugins": [ - "buildcfg/jsdoc/api-plugin", - "buildcfg/jsdoc/define-plugin" - ] -} diff --git a/buildcfg/jsdoc/externs/publish.js b/buildcfg/jsdoc/externs/publish.js deleted file mode 100644 index f050be590c..0000000000 --- a/buildcfg/jsdoc/externs/publish.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @fileoverview Generates a closure compiler externs file for exportable - * symbols (those with an api tag) and boolean defines (with a define tag and a - * default value). - */ -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); - - -/** - * Publish hook for the JSDoc template. Writes to stdout. - * @param {function} data The root of the Taffy DB containing doclet records. - * @param {Object} opts Options. - */ -exports.publish = function(data, opts) { - - // get all doclets with the "api" property or define (excluding enums, - // typedefs and events) - var docs = data( - [{define: {isObject: true}}, {api: {isString: true}}], - {isEnum: {'!is': true}}, - {kind: {'!is': 'typedef'}}, - {kind: {'!is': 'event'}}).get(); - - // get symbols data, filter out those that are members of private classes - var output = []; - var namespaces = {}; - var constructors = {}; - docs.filter(function(doc) { - var include = true; - var constructor = doc.memberof; - if (constructor && constructor.substr(-1) === '_') { - assert.strictEqual(doc.inherited, true, - 'Unexpected export on private class: ' + doc.longname); - include = false; - } - return include; - }).forEach(function(doc) { - - var parts = doc.longname.split('#')[0].split('.'); - parts.pop(); - var namespace = []; - parts.forEach(function(part) { - namespace.push(part); - var partialNamespace = namespace.join('.'); - if (!(partialNamespace in namespaces)) { - namespaces[partialNamespace] = true; - output.push('/**'); - output.push(' * @type {Object}'); - output.push(' */'); - output.push( - (namespace.length == 1 ? 'var ' : '') + partialNamespace + ';'); - output.push(''); - } - }); - - var signature = doc.longname; - if (signature.indexOf('#') > 0) { - signature = doc.longname.replace('#', '.prototype.'); - var constructor = doc.longname.split('#')[0]; - if (!(constructor in constructors)) { - constructors[constructor] = true; - output.push('/**'); - output.push(' * @constructor'); - output.push(' */'); - output.push(constructor + ' = function() {}'); - output.push(''); - } - } - - output.push('/**'); - if (doc.define) { - output.push(' * @define'); - output.push(' * @type {boolean}'); - output.push(' */'); - output.push(doc.longname + ';'); - } else { - if (doc.kind == 'class') { - output.push(' * @constructor'); - } - if (doc.type) { - var types = []; - doc.type.names.forEach(function(name) { - types.push(name); - }); - output.push(' * @type {' + types.join('|') + '}'); - } - var args = []; - if (doc.params) { - doc.params.forEach(function(param) { - args.push(param.name); - var names = []; - param.type.names.forEach(function(name) { - names.push(name); - }); - output.push(' * @param {' + - (param.variable ? '...' : '') + - names.join('|') + - (param.optional ? '=' : '') + - '} ' + param.name); - }); - } - if (doc.returns) { - var returnTypes = []; - doc.returns[0].type.names.forEach(function(name) { - returnTypes.push(name); - }); - output.push(' * @return {' + returnTypes.join('|') + '}'); - } - if (doc.tags) { - doc.tags.forEach(function(tag) { - if (tag.title == 'template') { - output.push(' * @' + tag.title + ' ' + tag.value); - } - }); - } - output.push(' */'); - if (doc.kind == 'function' || doc.kind == 'class') { - output.push(signature + ' = function(' + args.join(', ') + ') {};'); - } else { - output.push(signature); - } - } - output.push(''); - }); - - process.stdout.write(output.join('\n')); - -}; diff --git a/buildcfg/jsdoc/api-plugin.js b/buildcfg/jsdoc/info/api-plugin.js similarity index 100% rename from buildcfg/jsdoc/api-plugin.js rename to buildcfg/jsdoc/info/api-plugin.js diff --git a/buildcfg/jsdoc/info/conf.json b/buildcfg/jsdoc/info/conf.json index 3d2c6c6e53..c66f3993ab 100644 --- a/buildcfg/jsdoc/info/conf.json +++ b/buildcfg/jsdoc/info/conf.json @@ -10,7 +10,7 @@ "includePattern": "\\.js$" }, "plugins": [ - "buildcfg/jsdoc/api-plugin", - "buildcfg/jsdoc/define-plugin" + "buildcfg/jsdoc/info/api-plugin", + "buildcfg/jsdoc/info/define-plugin" ] } diff --git a/buildcfg/jsdoc/define-plugin.js b/buildcfg/jsdoc/info/define-plugin.js similarity index 100% rename from buildcfg/jsdoc/define-plugin.js rename to buildcfg/jsdoc/info/define-plugin.js diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index 8badcbf934..1a6e6113b9 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -44,12 +44,58 @@ exports.publish = function(data, opts) { default: doc.define.default }); } else { - symbols.push({ + var types; + var symbol = { name: doc.longname, kind: doc.kind, description: doc.classdesc || doc.description, path: path.join(doc.meta.path, doc.meta.filename) - }); + }; + if (doc.type) { + var types = []; + doc.type.names.forEach(function(name) { + types.push(name); + }); + symbol.types = types; + } + if (doc.params) { + var params = []; + doc.params.forEach(function(param) { + var paramInfo = { + name: param.name + }; + params.push(paramInfo); + var types = []; + param.type.names.forEach(function(name) { + types.push(name); + }); + paramInfo.types = types; + if (typeof param.variable == 'boolean') { + paramInfo.variable = param.variable; + } + if (typeof param.optional == 'boolean') { + paramInfo.optional = param.optional; + } + }); + symbol.params = params; + } + if (doc.returns) { + var returns = []; + doc.returns[0].type.names.forEach(function(name) { + returns.push(name); + }); + symbol.returns = returns; + } + if (doc.tags) { + doc.tags.every(function(tag) { + if (tag.title == 'template') { + symbol.template = tag.value; + return false; + } + return true; + }); + } + symbols.push(symbol); } }); diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 4498990432..7eaec497bb 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -1,157 +1,160 @@ var fs = require('fs'); var path = require('path'); -var spawn = require('child_process').spawn; var async = require('async'); var fse = require('fs-extra'); -var walk = require('walk').walk; +var nomnom = require('nomnom'); + +var generateInfo = require('./generate-info'); -var sourceDir = path.join(__dirname, '..', 'src'); var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); -var externsPath = path.join(__dirname, '..', 'build', 'ol-externs.js'); -var jsdoc = path.join(__dirname, '..', 'node_modules', '.bin', 'jsdoc'); -var jsdocConfig = path.join( - __dirname, '..', 'buildcfg', 'jsdoc', 'externs', 'conf.json'); /** - * Get the mtime of the externs file. - * @param {function(Error, Date)} callback Callback called with any - * error and the mtime of the externs file (zero date if it doesn't exist). + * Read the symbols from info file. + * @param {funciton(Error, Array., Array.)} callback Called + * with the patterns and symbols (or any error). */ -function getExternsTime(callback) { - fs.stat(externsPath, function(err, stats) { +function getSymbols(callback) { + generateInfo(function(err) { if (err) { - if (err.code === 'ENOENT') { - callback(null, new Date(0)); + callback(new Error('Trouble generating info: ' + err.message)); + return; + } + var symbols = require('../build/info.json').symbols; + callback(null, symbols); + }); +} + + +/** + * Generate externs code given a list symbols. + * @param {Array.} symbols List of symbols. + * @param {string|undefined} namespace Target object for exported symbols. + * @return {string} Export code. + */ +function generateExterns(symbols) { + var lines = []; + var namespaces = {}; + var constructors = {}; + + symbols.forEach(function(symbol) { + var parts = symbol.name.split('#')[0].split('.'); + parts.pop(); + var namespace = []; + parts.forEach(function(part) { + namespace.push(part); + var partialNamespace = namespace.join('.'); + if (!(partialNamespace in namespaces)) { + namespaces[partialNamespace] = true; + lines.push('/**'); + lines.push(' * @type {Object}'); + lines.push(' */'); + lines.push( + (namespace.length == 1 ? 'var ' : '') + partialNamespace + ';'); + lines.push('\n'); + } + }); + + var name = symbol.name; + if (name.indexOf('#') > 0) { + name = symbol.name.replace('#', '.prototype.'); + var constructor = symbol.name.split('#')[0]; + if (!(constructor in constructors)) { + constructors[constructor] = true; + lines.push('/**'); + lines.push(' * @constructor'); + lines.push(' */'); + lines.push(constructor + ' = function() {};'); + lines.push('\n'); + } + } + + lines.push('/**'); + if ('default' in symbol) { + lines.push(' * @define'); + lines.push(' * @type {boolean}'); + lines.push(' */'); + lines.push(symbol.name + ';'); + } else { + if (symbol.kind == 'class') { + lines.push(' * @constructor'); + } + if (symbol.types) { + lines.push(' * @type {' + symbol.types.join('|') + '}'); + } + var args = []; + if (symbol.params) { + symbol.params.forEach(function(param) { + args.push(param.name); + lines.push(' * @param {' + + (param.variable ? '...' : '') + + param.types.join('|') + + (param.optional ? '=' : '') + + '} ' + param.name); + }); + } + if (symbol.returns) { + lines.push(' * @return {' + symbol.returns.join('|') + '}'); + } + if (symbol.template) { + lines.push(' * @template ' + symbol.template); + } + lines.push(' */'); + if (symbol.kind == 'function' || symbol.kind == 'class') { + lines.push(name + ' = function(' + args.join(', ') + ') {};'); } else { - callback(err); - } - } else { - callback(null, stats.mtime); - } - }); -} - - -/** - * Generate a list of all .js paths in the source directory if any are newer - * than the provided date. - * @param {Date} date Modification time of externs file. - * @param {function(Error, Array.)} callback Called with any - * error and the array of source paths (empty if none newer). - */ -function getNewer(date, callback) { - var paths = []; - var newer = false; - - var walker = walk(sourceDir); - walker.on('file', function(root, stats, next) { - var sourcePath = path.join(root, stats.name); - if (/\.js$/.test(sourcePath)) { - paths.push(sourcePath); - if (stats.mtime > date) { - newer = true; + lines.push(name + ';'); } } - next(); - }); - walker.on('errors', function() { - callback(new Error('Trouble walking ' + sourceDir)); - }); - walker.on('end', function() { - callback(null, newer ? paths : []); + lines.push('\n'); }); + + return lines.join('\n'); } /** - * Spawn JSDoc. - * @param {Array.} paths Paths to source files. - * @param {function(Error, string)} callback Callback called with any error and - * the JSDoc output (new metadata). If provided with an empty list of paths - * the callback will be called with null. - */ -function spawnJSDoc(paths, callback) { - if (paths.length === 0) { - process.nextTick(function() { - callback(null, null); - }); - return; - } - - var output = ''; - var errors = ''; - var cwd = path.join(__dirname, '..'); - var child = spawn(jsdoc, ['-c', jsdocConfig].concat(paths), {cwd: cwd}); - - child.stdout.on('data', function(data) { - output += String(data); - }); - - child.stderr.on('data', function(data) { - errors += String(data); - }); - - child.on('exit', function(code) { - if (code) { - callback(new Error(errors || 'JSDoc failed with no output')); - } else { - callback(null, output); - } - }); -} - - -/** - * Write externs file consisting of externs/olx.js and the JSDoc generated - * externs. - * @param {Object} externs JSDoc generated externs. - * @param {function(Error)} callback Callback. - */ -function writeExterns(externs, callback) { - if (externs) { - var olx; - try { - olx = fs.readFileSync(olxPath, {encoding: 'utf-8'}) - .replace(/ \* @api ?(.*)?(\r\n|\n|\r)/gm, ''); - } catch(e) { - process.nextTick(function() { - callback(null); - }); - } - fse.outputFile(externsPath, olx + '\n\n' + externs, callback); - } else { - process.nextTick(function() { - callback(null); - }); - } -} - - -/** - * Determine if source files have been changed, run JSDoc and write updated - * externs if there are any changes. + * Generate the exports code. * - * @param {function(Error)} callback Called when the externs file has been - * written (or an error occurs). + * @param {function(Error, string)} callback Called with the exports code or any + * error generating it. */ function main(callback) { async.waterfall([ - getExternsTime, - getNewer, - spawnJSDoc, - writeExterns + getSymbols, + function(symbols, done) { + var code, err; + try { + var olx = fs.readFileSync(olxPath, {encoding: 'utf-8'}) + .replace(/ \* @api ?(.*)?(\r\n|\n|\r)/gm, ''); + code = olx + '\n\n' + generateExterns(symbols); + } catch (e) { + err = e; + } + done(err, code); + } ], callback); } /** - * If running this module directly, read the config file and call the main - * function. + * If running this module directly, read the config file, call the main + * function, and write the output file. */ if (require.main === module) { - main(function(err) { + var options = nomnom.options({ + output: { + position: 0, + required: true, + help: 'Output path for the generated externs file.' + } + }).parse(); + + async.waterfall([ + main, + fse.outputFile.bind(fse, options.output) + ], function(err) { if (err) { console.error(err.message); process.exit(1); diff --git a/tasks/readme.md b/tasks/readme.md index 1e5826242e..ad3fff9f8c 100644 --- a/tasks/readme.md +++ b/tasks/readme.md @@ -91,7 +91,10 @@ Called internally to generate a `build/exports.js` file optionally with a limite ## `generate-externs.js` -Can be called to generate a Closure externs file for the full OpenLayers 3 API. The externs file will be placed as `ol-externs.js` in the `build/` directory. +Can be called to generate a Closure externs file for the full OpenLayers 3 API. +See the `--help` option for more detail. + + node tasks/generate-externs.js --help This is useful for projects that use the Closure Compiler to build, but want to use OpenLayers 3 as external library rather than building together with OpenLayers 3. From 66bde546c039e2f387b4713cfed282dee0b8974d Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 13:30:38 +0200 Subject: [PATCH 06/25] Avoid duplicate constructors --- tasks/generate-externs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 7eaec497bb..73066052b0 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -78,6 +78,7 @@ function generateExterns(symbols) { lines.push(symbol.name + ';'); } else { if (symbol.kind == 'class') { + constructors[name] = true; lines.push(' * @constructor'); } if (symbol.types) { From 9517c0d4338e7600e5fb79f3c339a9af7ea1c76c Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 13:31:02 +0200 Subject: [PATCH 07/25] Use 'Function' type instead of 'function' --- buildcfg/jsdoc/info/publish.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index 1a6e6113b9..fc6105ae4b 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -52,9 +52,9 @@ exports.publish = function(data, opts) { path: path.join(doc.meta.path, doc.meta.filename) }; if (doc.type) { - var types = []; + types = []; doc.type.names.forEach(function(name) { - types.push(name); + types.push(name.replace(/^function$/, 'Function')); }); symbol.types = types; } @@ -67,7 +67,7 @@ exports.publish = function(data, opts) { params.push(paramInfo); var types = []; param.type.names.forEach(function(name) { - types.push(name); + types.push(name.replace(/^function$/, 'Function')); }); paramInfo.types = types; if (typeof param.variable == 'boolean') { @@ -82,7 +82,7 @@ exports.publish = function(data, opts) { if (doc.returns) { var returns = []; doc.returns[0].type.names.forEach(function(name) { - returns.push(name); + returns.push(name.replace(/^function$/, 'Function')); }); symbol.returns = returns; } From 94e291169712b56b18d70a510e91272958ddc74e Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 14:13:39 +0200 Subject: [PATCH 08/25] Fix function type so the function is optional, not the return value --- src/ol/map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/map.js b/src/ol/map.js index f4760cef4f..b9d5b4d5ad 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -554,7 +554,7 @@ ol.Map.prototype.disposeInternal = function() { * be `null`. To stop detection, callback functions can return a truthy * value. * @param {S=} opt_this Value to use as `this` when executing `callback`. - * @param {function(this: U, ol.layer.Layer): boolean=} opt_layerFilter Layer + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer * filter function, only layers which are visible and for which this * function returns `true` will be tested for features. By default, all * visible layers will be tested. Feature overlays will always be tested. From 46b74cedf807e77b6f349f7bef150068700dc657 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 14:14:09 +0200 Subject: [PATCH 09/25] Only add object type definition when there is no constructor --- tasks/generate-externs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 73066052b0..5cf77e5126 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -45,7 +45,8 @@ function generateExterns(symbols) { parts.forEach(function(part) { namespace.push(part); var partialNamespace = namespace.join('.'); - if (!(partialNamespace in namespaces)) { + if (!(partialNamespace in namespaces || + partialNamespace in constructors)) { namespaces[partialNamespace] = true; lines.push('/**'); lines.push(' * @type {Object}'); From 26dae36a0b51a194499b5d0ec2297453abbece85 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 15:56:37 +0200 Subject: [PATCH 10/25] Extract types in a separate function --- buildcfg/jsdoc/info/publish.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index fc6105ae4b..5555382ebc 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -13,6 +13,15 @@ var path = require('path'); * @param {Object} opts Options. */ exports.publish = function(data, opts) { + + function getTypes(data) { + var types = []; + data.forEach(function(name) { + types.push(name.replace(/^function$/, 'Function')); + }); + return types; + } + var cwd = process.cwd(); // get all doclets with the "api" property or define (excluding enums, @@ -52,11 +61,7 @@ exports.publish = function(data, opts) { path: path.join(doc.meta.path, doc.meta.filename) }; if (doc.type) { - types = []; - doc.type.names.forEach(function(name) { - types.push(name.replace(/^function$/, 'Function')); - }); - symbol.types = types; + symbol.types = getTypes(doc.type.names); } if (doc.params) { var params = []; @@ -65,11 +70,7 @@ exports.publish = function(data, opts) { name: param.name }; params.push(paramInfo); - var types = []; - param.type.names.forEach(function(name) { - types.push(name.replace(/^function$/, 'Function')); - }); - paramInfo.types = types; + paramInfo.types = getTypes(param.type.names); if (typeof param.variable == 'boolean') { paramInfo.variable = param.variable; } @@ -80,11 +81,7 @@ exports.publish = function(data, opts) { symbol.params = params; } if (doc.returns) { - var returns = []; - doc.returns[0].type.names.forEach(function(name) { - returns.push(name.replace(/^function$/, 'Function')); - }); - symbol.returns = returns; + symbol.returns = getTypes(doc.returns[0].type.names); } if (doc.tags) { doc.tags.every(function(tag) { From b2059b85d2f8b825fb4ab0b06a91dfeea9c36590 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 15:57:45 +0200 Subject: [PATCH 11/25] Add data for typedefs to info.json --- buildcfg/jsdoc/info/publish.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index 5555382ebc..5ec72c2fbd 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -28,13 +28,12 @@ exports.publish = function(data, opts) { // typedefs and events) var docs = data( [{define: {isObject: true}}, {api: {isString: true}}], - {isEnum: {'!is': true}}, - {kind: {'!is': 'typedef'}}, {kind: {'!is': 'event'}}).get(); // get symbols data, filter out those that are members of private classes var symbols = []; var defines = []; + var typedefs = []; docs.filter(function(doc) { var include = true; var constructor = doc.memberof; @@ -52,6 +51,11 @@ exports.publish = function(data, opts) { path: path.join(doc.meta.path, doc.meta.filename), default: doc.define.default }); + } else if (doc.kind == 'typedef' || doc.isEnum === true) { + typedefs.push({ + name: doc.longname, + types: getTypes(doc.type.names) + }); } else { var types; var symbol = { @@ -97,6 +101,10 @@ exports.publish = function(data, opts) { }); process.stdout.write( - JSON.stringify({symbols: symbols, defines: defines}, null, 2)); + JSON.stringify({ + symbols: symbols, + defines: defines, + typedefs: typedefs + }, null, 2)); }; From a40170feb7e792a02539ef3b0465c0aea5530390 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 15:58:00 +0200 Subject: [PATCH 12/25] Include typedefs in externs --- tasks/generate-externs.js | 85 ++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 5cf77e5126..1719bca3f4 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -15,29 +15,39 @@ var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); * @param {funciton(Error, Array., Array.)} callback Called * with the patterns and symbols (or any error). */ -function getSymbols(callback) { +function getTypedefsAndSymbols(callback) { generateInfo(function(err) { if (err) { callback(new Error('Trouble generating info: ' + err.message)); return; } + var typedefs = require('../build/info.json').typedefs; var symbols = require('../build/info.json').symbols; - callback(null, symbols); + callback(null, typedefs, symbols); }); } /** * Generate externs code given a list symbols. + * @param {Array.} typedefs List of typedefs. * @param {Array.} symbols List of symbols. * @param {string|undefined} namespace Target object for exported symbols. * @return {string} Export code. */ -function generateExterns(symbols) { +function generateExterns(typedefs, symbols) { var lines = []; var namespaces = {}; var constructors = {}; + typedefs.forEach(function(typedef) { + lines.push('/**'); + lines.push(' * @typedef {' + typedef.types.join('|') + '}'); + lines.push(' */'); + lines.push(typedef.name + ';'); + lines.push('\n'); + }); + symbols.forEach(function(symbol) { var parts = symbol.name.split('#')[0].split('.'); parts.pop(); @@ -72,42 +82,35 @@ function generateExterns(symbols) { } lines.push('/**'); - if ('default' in symbol) { - lines.push(' * @define'); - lines.push(' * @type {boolean}'); - lines.push(' */'); - lines.push(symbol.name + ';'); + if (symbol.kind == 'class') { + constructors[name] = true; + lines.push(' * @constructor'); + } + if (symbol.types) { + lines.push(' * @type {' + symbol.types.join('|') + '}'); + } + var args = []; + if (symbol.params) { + symbol.params.forEach(function(param) { + args.push(param.name); + lines.push(' * @param {' + + (param.variable ? '...' : '') + + param.types.join('|') + + (param.optional ? '=' : '') + + '} ' + param.name); + }); + } + if (symbol.returns) { + lines.push(' * @return {' + symbol.returns.join('|') + '}'); + } + if (symbol.template) { + lines.push(' * @template ' + symbol.template); + } + lines.push(' */'); + if (symbol.kind == 'function' || symbol.kind == 'class') { + lines.push(name + ' = function(' + args.join(', ') + ') {};'); } else { - if (symbol.kind == 'class') { - constructors[name] = true; - lines.push(' * @constructor'); - } - if (symbol.types) { - lines.push(' * @type {' + symbol.types.join('|') + '}'); - } - var args = []; - if (symbol.params) { - symbol.params.forEach(function(param) { - args.push(param.name); - lines.push(' * @param {' + - (param.variable ? '...' : '') + - param.types.join('|') + - (param.optional ? '=' : '') + - '} ' + param.name); - }); - } - if (symbol.returns) { - lines.push(' * @return {' + symbol.returns.join('|') + '}'); - } - if (symbol.template) { - lines.push(' * @template ' + symbol.template); - } - lines.push(' */'); - if (symbol.kind == 'function' || symbol.kind == 'class') { - lines.push(name + ' = function(' + args.join(', ') + ') {};'); - } else { - lines.push(name + ';'); - } + lines.push(name + ';'); } lines.push('\n'); }); @@ -124,13 +127,13 @@ function generateExterns(symbols) { */ function main(callback) { async.waterfall([ - getSymbols, - function(symbols, done) { + getTypedefsAndSymbols, + function(typedefs, symbols, done) { var code, err; try { var olx = fs.readFileSync(olxPath, {encoding: 'utf-8'}) .replace(/ \* @api ?(.*)?(\r\n|\n|\r)/gm, ''); - code = olx + '\n\n' + generateExterns(symbols); + code = olx + '\n\n' + generateExterns(typedefs, symbols); } catch (e) { err = e; } From 33a627b7ce2b0c518202104ee96436deb7fa64cb Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 20:33:35 +0200 Subject: [PATCH 13/25] Include externs/olx.js in the info generation --- tasks/generate-info.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasks/generate-info.js b/tasks/generate-info.js index f94d22980d..1dfea9c0a6 100644 --- a/tasks/generate-info.js +++ b/tasks/generate-info.js @@ -7,6 +7,7 @@ var fse = require('fs-extra'); var walk = require('walk').walk; var sourceDir = path.join(__dirname, '..', 'src'); +var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); var infoPath = path.join(__dirname, '..', 'build', 'info.json'); var jsdoc = path.join(__dirname, '..', 'node_modules', '.bin', 'jsdoc'); var jsdocConfig = path.join( @@ -41,8 +42,8 @@ function getInfoTime(callback) { * error and the array of source paths (empty if none newer). */ function getNewer(date, callback) { - var paths = []; var newer = false; + var paths = [olxPath]; var walker = walk(sourceDir); walker.on('file', function(root, stats, next) { From 58f857bbb6be4262ebfa00f672dbb86dd3d42075 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 20:34:17 +0200 Subject: [PATCH 14/25] Test whether externs/olx.js is newer than build/info.json --- tasks/generate-info.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tasks/generate-info.js b/tasks/generate-info.js index 1dfea9c0a6..883a5fa978 100644 --- a/tasks/generate-info.js +++ b/tasks/generate-info.js @@ -34,15 +34,33 @@ function getInfoTime(callback) { } +/** + * Test whether externs/olx.js is newer than the provided date. + * @param {Date} date Modification time of info file. + * @param {function(Error, Date, boolen)} callback Called with any + * error, the mtime of the info file (zero date if it doesn't exist), and + * whether externs/olx.js is newer than that date. + */ +function getOlxNewer(date, callback) { + fs.stat(olxPath, function(err, stats) { + if (err) { + callback(new Error('Trouble reading ' + olxPath)); + } else { + callback(null, date, stats.mtime > date); + } + }); +} + + /** * Generate a list of all .js paths in the source directory if any are newer * than the provided date. * @param {Date} date Modification time of info file. + * @param {boolean} newer Whether externs/olx.js is newer than date. * @param {function(Error, Array.)} callback Called with any * error and the array of source paths (empty if none newer). */ -function getNewer(date, callback) { - var newer = false; +function getNewer(date, newer, callback) { var paths = [olxPath]; var walker = walk(sourceDir); @@ -220,6 +238,7 @@ function writeInfo(info, callback) { function main(callback) { async.waterfall([ getInfoTime, + getOlxNewer, getNewer, spawnJSDoc, addSymbolProvides, From f0242a76c45fe483900f46f06f20d5343923b62b Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 20:36:45 +0200 Subject: [PATCH 15/25] Include typedefs and types from olx.js --- buildcfg/jsdoc/info/publish.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index 5ec72c2fbd..a6fd601681 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -24,10 +24,14 @@ exports.publish = function(data, opts) { var cwd = process.cwd(); - // get all doclets with the "api" property or define (excluding enums, - // typedefs and events) + // get all doclets with the "api" property or define (excluding events) or + // with olx namespace var docs = data( - [{define: {isObject: true}}, {api: {isString: true}}], + [ + {define: {isObject: true}}, + {api: {isString: true}}, + {longname: {left: 'olx.'}} + ], {kind: {'!is': 'event'}}).get(); // get symbols data, filter out those that are members of private classes @@ -44,7 +48,20 @@ exports.publish = function(data, opts) { } return include; }).forEach(function(doc) { - if (doc.define) { + if (doc.longname.indexOf('olx.') === 0) { + if (doc.kind == 'typedef') { + typedefs.push({ + name: doc.longname, + types: ['{}'] + }); + } else { + var type = typedefs[typedefs.length - 1].types[0]; + typedefs[typedefs.length - 1].types[0] = type + .replace(/\}$/, ', ' + doc.longname.split('#')[1] + + ': (' + getTypes(doc.type.names).join('|') + ')}') + .replace('{, ', '{'); + } + } else if (doc.define) { defines.push({ name: doc.longname, description: doc.description, From 53e12e7e47cd36f1d0b6f4e747fa4aa6130a6eef Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 20:37:10 +0200 Subject: [PATCH 16/25] Add typedefs for externs/olx.js contents --- tasks/generate-externs.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 1719bca3f4..4e99d70c2f 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -40,16 +40,8 @@ function generateExterns(typedefs, symbols) { var namespaces = {}; var constructors = {}; - typedefs.forEach(function(typedef) { - lines.push('/**'); - lines.push(' * @typedef {' + typedef.types.join('|') + '}'); - lines.push(' */'); - lines.push(typedef.name + ';'); - lines.push('\n'); - }); - - symbols.forEach(function(symbol) { - var parts = symbol.name.split('#')[0].split('.'); + function addNamespaces(name) { + var parts = name.split('.'); parts.pop(); var namespace = []; parts.forEach(function(part) { @@ -66,6 +58,19 @@ function generateExterns(typedefs, symbols) { lines.push('\n'); } }); + } + + typedefs.forEach(function(typedef) { + addNamespaces(typedef.name); + lines.push('/**'); + lines.push(' * @typedef {' + typedef.types.join('|') + '}'); + lines.push(' */'); + lines.push(typedef.name + ';'); + lines.push('\n'); + }); + + symbols.forEach(function(symbol) { + addNamespaces(symbol.name.split('#')[0]); var name = symbol.name; if (name.indexOf('#') > 0) { @@ -131,9 +136,7 @@ function main(callback) { function(typedefs, symbols, done) { var code, err; try { - var olx = fs.readFileSync(olxPath, {encoding: 'utf-8'}) - .replace(/ \* @api ?(.*)?(\r\n|\n|\r)/gm, ''); - code = olx + '\n\n' + generateExterns(typedefs, symbols); + code = generateExterns(typedefs, symbols); } catch (e) { err = e; } From 9575396aadd44e4d80401f4b35a5195726fc6c24 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 4 Aug 2014 20:37:23 +0200 Subject: [PATCH 17/25] Fix types in olx.js --- externs/olx.js | 53 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index eee156fdec..d6db0b08ea 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -106,6 +106,20 @@ olx.GeolocationOptions.prototype.projection; olx.LogoOptions; +/** + * Link url for the logo. Will be followed when the logo is clicked. + * @type {string} + */ +olx.LogoOptions.prototype.href; + + +/** + * Image src for the logo + * @type {string} + */ +olx.LogoOptions.prototype.src; + + /** * @typedef {{map: (ol.Map|undefined), * maxLines: (number|undefined), @@ -1176,6 +1190,7 @@ olx.format.PolylineOptions; /** * The factor by which the coordinates values will be scaled. * Default is `1e5`. + * @type {number} */ olx.format.PolylineOptions.prototype.factor; @@ -2769,7 +2784,7 @@ olx.source.FormatVectorOptions.prototype.format; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.FormatVectorOptions.prototype.logo; @@ -2811,7 +2826,7 @@ olx.source.GeoJSONOptions.prototype.defaultProjection; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.GeoJSONOptions.prototype.logo; @@ -2882,7 +2897,7 @@ olx.source.GPXOptions.prototype.doc; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.GPXOptions.prototype.logo; @@ -2957,7 +2972,7 @@ olx.source.TileImageOptions.prototype.crossOrigin; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.TileImageOptions.prototype.logo; @@ -3055,7 +3070,7 @@ olx.source.TileVectorOptions.prototype.format; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.TileVectorOptions.prototype.logo; @@ -3133,7 +3148,7 @@ olx.source.TopoJSONOptions.prototype.defaultProjection; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.TopoJSONOptions.prototype.logo; @@ -3333,7 +3348,7 @@ olx.source.KMLOptions.prototype.doc; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.KMLOptions.prototype.logo; @@ -3504,7 +3519,7 @@ olx.source.OSMXMLOptions.prototype.doc; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.OSMXMLOptions.prototype.logo; @@ -3587,7 +3602,7 @@ olx.source.ImageCanvasOptions.prototype.canvasFunction; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ImageCanvasOptions.prototype.logo; @@ -3644,7 +3659,7 @@ olx.source.ImageVectorOptions.prototype.attributions; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ImageVectorOptions.prototype.logo; @@ -3735,7 +3750,7 @@ olx.source.ImageWMSOptions.prototype.serverType; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ImageWMSOptions.prototype.logo; @@ -3875,7 +3890,7 @@ olx.source.ImageStaticOptions.prototype.imageSize; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ImageStaticOptions.prototype.logo; @@ -3937,7 +3952,7 @@ olx.source.ServerVectorOptions.prototype.strategy; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ServerVectorOptions.prototype.logo; @@ -4045,7 +4060,7 @@ olx.source.TileWMSOptions.prototype.hidpi; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.TileWMSOptions.prototype.logo; @@ -4132,7 +4147,7 @@ olx.source.VectorOptions.prototype.features; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.VectorOptions.prototype.logo; @@ -4198,7 +4213,7 @@ olx.source.StaticVectorOptions.prototype.format; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.StaticVectorOptions.prototype.logo; @@ -4284,7 +4299,7 @@ olx.source.WMTSOptions.prototype.crossOrigin; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.WMTSOptions.prototype.logo; @@ -4424,7 +4439,7 @@ olx.source.XYZOptions.prototype.crossOrigin; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.XYZOptions.prototype.logo; @@ -4524,7 +4539,7 @@ olx.source.ZoomifyOptions.prototype.crossOrigin; /** * Logo. - * @type{string|olx.LogoOptions|undefined} + * @type {string|olx.LogoOptions|undefined} */ olx.source.ZoomifyOptions.prototype.logo; From e8eebd8846a89b60c8522ee00118f95b3ab38bfc Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Thu, 7 Aug 2014 09:12:07 +0200 Subject: [PATCH 18/25] Allow to provide externs for inclusion in info.json --- buildcfg/jsdoc/info/publish.js | 13 ++++++----- tasks/generate-externs.js | 40 ++++++++++++++++++---------------- tasks/generate-info.js | 34 ++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index a6fd601681..f9c8c28ac7 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -22,15 +22,15 @@ exports.publish = function(data, opts) { return types; } - var cwd = process.cwd(); - // get all doclets with the "api" property or define (excluding events) or // with olx namespace var docs = data( [ {define: {isObject: true}}, {api: {isString: true}}, - {longname: {left: 'olx.'}} + function() { + return this.meta && (/[\\\/]externs$/).test(this.meta.path); + } ], {kind: {'!is': 'event'}}).get(); @@ -38,6 +38,7 @@ exports.publish = function(data, opts) { var symbols = []; var defines = []; var typedefs = []; + var externs = []; docs.filter(function(doc) { var include = true; var constructor = doc.memberof; @@ -113,7 +114,8 @@ exports.publish = function(data, opts) { return true; }); } - symbols.push(symbol); + var target = (/[\\\/]externs$/).test(doc.meta.path) ? externs : symbols; + target.push(symbol); } }); @@ -121,7 +123,8 @@ exports.publish = function(data, opts) { JSON.stringify({ symbols: symbols, defines: defines, - typedefs: typedefs + typedefs: typedefs, + externs: externs }, null, 2)); }; diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 4e99d70c2f..b2ebdd9f34 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -7,15 +7,12 @@ var nomnom = require('nomnom'); var generateInfo = require('./generate-info'); -var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); - - /** * Read the symbols from info file. * @param {funciton(Error, Array., Array.)} callback Called * with the patterns and symbols (or any error). */ -function getTypedefsAndSymbols(callback) { +function getInfo(callback) { generateInfo(function(err) { if (err) { callback(new Error('Trouble generating info: ' + err.message)); @@ -23,7 +20,8 @@ function getTypedefsAndSymbols(callback) { } var typedefs = require('../build/info.json').typedefs; var symbols = require('../build/info.json').symbols; - callback(null, typedefs, symbols); + var externs = require('../build/info.json').externs; + callback(null, typedefs, symbols, externs); }); } @@ -32,10 +30,11 @@ function getTypedefsAndSymbols(callback) { * Generate externs code given a list symbols. * @param {Array.} typedefs List of typedefs. * @param {Array.} symbols List of symbols. + * @param {Array.} externs List of externs. * @param {string|undefined} namespace Target object for exported symbols. * @return {string} Export code. */ -function generateExterns(typedefs, symbols) { +function generateExterns(typedefs, symbols, externs) { var lines = []; var namespaces = {}; var constructors = {}; @@ -60,16 +59,7 @@ function generateExterns(typedefs, symbols) { }); } - typedefs.forEach(function(typedef) { - addNamespaces(typedef.name); - lines.push('/**'); - lines.push(' * @typedef {' + typedef.types.join('|') + '}'); - lines.push(' */'); - lines.push(typedef.name + ';'); - lines.push('\n'); - }); - - symbols.forEach(function(symbol) { + function processSymbol(symbol) { addNamespaces(symbol.name.split('#')[0]); var name = symbol.name; @@ -118,7 +108,19 @@ function generateExterns(typedefs, symbols) { lines.push(name + ';'); } lines.push('\n'); + } + + typedefs.forEach(function(typedef) { + addNamespaces(typedef.name); + lines.push('/**'); + lines.push(' * @typedef {' + typedef.types.join('|') + '}'); + lines.push(' */'); + lines.push(typedef.name + ';'); + lines.push('\n'); }); + + externs.forEach(processSymbol); + symbols.forEach(processSymbol); return lines.join('\n'); } @@ -132,11 +134,11 @@ function generateExterns(typedefs, symbols) { */ function main(callback) { async.waterfall([ - getTypedefsAndSymbols, - function(typedefs, symbols, done) { + getInfo, + function(typedefs, symbols, externs, done) { var code, err; try { - code = generateExterns(typedefs, symbols); + code = generateExterns(typedefs, symbols, externs); } catch (e) { err = e; } diff --git a/tasks/generate-info.js b/tasks/generate-info.js index 883a5fa978..7f6832115b 100644 --- a/tasks/generate-info.js +++ b/tasks/generate-info.js @@ -7,7 +7,11 @@ var fse = require('fs-extra'); var walk = require('walk').walk; var sourceDir = path.join(__dirname, '..', 'src'); -var olxPath = path.join(__dirname, '..', 'externs', 'olx.js'); +var externsDir = path.join(__dirname, '..', 'externs'); +var externsPaths = [ + path.join(externsDir, 'olx.js'), + path.join(externsDir, 'geojson.js') +]; var infoPath = path.join(__dirname, '..', 'build', 'info.json'); var jsdoc = path.join(__dirname, '..', 'node_modules', '.bin', 'jsdoc'); var jsdocConfig = path.join( @@ -41,13 +45,23 @@ function getInfoTime(callback) { * error, the mtime of the info file (zero date if it doesn't exist), and * whether externs/olx.js is newer than that date. */ -function getOlxNewer(date, callback) { - fs.stat(olxPath, function(err, stats) { - if (err) { - callback(new Error('Trouble reading ' + olxPath)); - } else { - callback(null, date, stats.mtime > date); - } +function getNewerExterns(date, callback) { + var newer = false; + var walker = walk(externsDir); + walker.on('file', function(root, stats, next) { + var sourcePath = path.join(root, stats.name); + externsPaths.forEach(function(path) { + if (sourcePath == path && stats.mtime > date) { + newer = true; + } + }); + next(); + }); + walker.on('errors', function() { + callback(new Error('Trouble walking ' + sourceDir)); + }); + walker.on('end', function() { + callback(null, date, newer); }); } @@ -61,7 +75,7 @@ function getOlxNewer(date, callback) { * error and the array of source paths (empty if none newer). */ function getNewer(date, newer, callback) { - var paths = [olxPath]; + var paths = [].concat(externsPaths); var walker = walk(sourceDir); walker.on('file', function(root, stats, next) { @@ -238,7 +252,7 @@ function writeInfo(info, callback) { function main(callback) { async.waterfall([ getInfoTime, - getOlxNewer, + getNewerExterns, getNewer, spawnJSDoc, addSymbolProvides, From 9e814f119fd95da69dcf3d89bf15d4c3b41ddc29 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 13:36:38 +0200 Subject: [PATCH 19/25] Do not process file type doclets --- buildcfg/jsdoc/info/publish.js | 1 + 1 file changed, 1 insertion(+) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index f9c8c28ac7..da797f5f4c 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -32,6 +32,7 @@ exports.publish = function(data, opts) { return this.meta && (/[\\\/]externs$/).test(this.meta.path); } ], + {kind: {'!is': 'file'}}, {kind: {'!is': 'event'}}).get(); // get symbols data, filter out those that are members of private classes From a35f3616836b55bdf586b62ba00335ea8a0a6fe4 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 13:37:36 +0200 Subject: [PATCH 20/25] Build types only for olx.* symbols from externs --- buildcfg/jsdoc/info/publish.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index da797f5f4c..b8f85b37b4 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -50,7 +50,8 @@ exports.publish = function(data, opts) { } return include; }).forEach(function(doc) { - if (doc.longname.indexOf('olx.') === 0) { + var isExterns = (/[\\\/]externs$/).test(doc.meta.path); + if (isExterns && doc.longname.indexOf('olx.') === 0) { if (doc.kind == 'typedef') { typedefs.push({ name: doc.longname, @@ -115,7 +116,7 @@ exports.publish = function(data, opts) { return true; }); } - var target = (/[\\\/]externs$/).test(doc.meta.path) ? externs : symbols; + var target = isExterns ? externs : symbols; target.push(symbol); } }); From 9780e9f0c0974688b570407530653b368bfb7622 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 13:37:51 +0200 Subject: [PATCH 21/25] Dereference typedef --- buildcfg/jsdoc/info/publish.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/buildcfg/jsdoc/info/publish.js b/buildcfg/jsdoc/info/publish.js index b8f85b37b4..e2692d482a 100644 --- a/buildcfg/jsdoc/info/publish.js +++ b/buildcfg/jsdoc/info/publish.js @@ -58,8 +58,9 @@ exports.publish = function(data, opts) { types: ['{}'] }); } else { - var type = typedefs[typedefs.length - 1].types[0]; - typedefs[typedefs.length - 1].types[0] = type + var typedef = typedefs[typedefs.length - 1]; + var type = typedef.types[0]; + typedef.types[0] = type .replace(/\}$/, ', ' + doc.longname.split('#')[1] + ': (' + getTypes(doc.type.names).join('|') + ')}') .replace('{, ', '{'); From 63abbe2597b03303b7edc3bb33c48f4e73787383 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 13:39:00 +0200 Subject: [PATCH 22/25] Prepend 'var ' for root symbols --- tasks/generate-externs.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index b2ebdd9f34..0fbd7bb050 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -48,17 +48,23 @@ function generateExterns(typedefs, symbols, externs) { var partialNamespace = namespace.join('.'); if (!(partialNamespace in namespaces || partialNamespace in constructors)) { - namespaces[partialNamespace] = true; lines.push('/**'); lines.push(' * @type {Object}'); lines.push(' */'); - lines.push( - (namespace.length == 1 ? 'var ' : '') + partialNamespace + ';'); + lines.push(nameToJS(partialNamespace) + ';'); lines.push('\n'); } }); } + function nameToJS(name) { + if (name.indexOf('.') == -1) { + namespaces[name] = true; + name = 'var ' + name; + } + return name; + } + function processSymbol(symbol) { addNamespaces(symbol.name.split('#')[0]); @@ -71,7 +77,7 @@ function generateExterns(typedefs, symbols, externs) { lines.push('/**'); lines.push(' * @constructor'); lines.push(' */'); - lines.push(constructor + ' = function() {};'); + lines.push(nameToJS(constructor) + ' = function() {};'); lines.push('\n'); } } @@ -103,23 +109,24 @@ function generateExterns(typedefs, symbols, externs) { } lines.push(' */'); if (symbol.kind == 'function' || symbol.kind == 'class') { - lines.push(name + ' = function(' + args.join(', ') + ') {};'); + lines.push(nameToJS(name) + ' = function(' + args.join(', ') + ') {};'); } else { - lines.push(name + ';'); + lines.push(nameToJS(name) + ';'); } lines.push('\n'); } + externs.forEach(processSymbol); + typedefs.forEach(function(typedef) { addNamespaces(typedef.name); lines.push('/**'); lines.push(' * @typedef {' + typedef.types.join('|') + '}'); lines.push(' */'); - lines.push(typedef.name + ';'); + lines.push(nameToJS(typedef.name) + ';'); lines.push('\n'); }); - externs.forEach(processSymbol); symbols.forEach(processSymbol); return lines.join('\n'); From 8a56f455f1ecce19ca74585ab8f1ee8a20fd3501 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 14:09:54 +0200 Subject: [PATCH 23/25] Define constructor before its members --- externs/geojson.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/externs/geojson.js b/externs/geojson.js index bec6c7b428..984d0728e4 100644 --- a/externs/geojson.js +++ b/externs/geojson.js @@ -52,6 +52,13 @@ GeoJSONCRS.prototype.properties; var GeoJSONCRSCode = function() {}; + +/** + * @constructor + */ +var GeoJSONCRSName = function() {}; + + /** * @type {string} * TODO: remove this when http://jira.codehaus.org/browse/GEOS-5996 is fixed @@ -60,13 +67,6 @@ var GeoJSONCRSCode = function() {}; GeoJSONCRSName.prototype.code; - -/** - * @constructor - */ -var GeoJSONCRSName = function() {}; - - /** * @type {string} */ From e294ca186814fe1f180486141cb674ef138be036 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 8 Aug 2014 18:02:47 +0200 Subject: [PATCH 24/25] Keep track of all symbols This avoids that we add parent namespaces of symbols that were already added because they have their own definition in the externs. --- tasks/generate-externs.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index 0fbd7bb050..e98cfbf56b 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -36,7 +36,7 @@ function getInfo(callback) { */ function generateExterns(typedefs, symbols, externs) { var lines = []; - var namespaces = {}; + var processedSymbols = {}; var constructors = {}; function addNamespaces(name) { @@ -46,7 +46,7 @@ function generateExterns(typedefs, symbols, externs) { parts.forEach(function(part) { namespace.push(part); var partialNamespace = namespace.join('.'); - if (!(partialNamespace in namespaces || + if (!(partialNamespace in processedSymbols || partialNamespace in constructors)) { lines.push('/**'); lines.push(' * @type {Object}'); @@ -58,8 +58,8 @@ function generateExterns(typedefs, symbols, externs) { } function nameToJS(name) { + processedSymbols[name] = true; if (name.indexOf('.') == -1) { - namespaces[name] = true; name = 'var ' + name; } return name; From 286db65d345482acc817d60a4dbd5e386ccb46e7 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sat, 9 Aug 2014 09:05:54 +0200 Subject: [PATCH 25/25] Use {*} instead of {goog.*} types in externs file --- tasks/generate-externs.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tasks/generate-externs.js b/tasks/generate-externs.js index e98cfbf56b..1962883aa0 100644 --- a/tasks/generate-externs.js +++ b/tasks/generate-externs.js @@ -65,6 +65,14 @@ function generateExterns(typedefs, symbols, externs) { return name; } + function noGoogTypes(typesWithGoog) { + typesWithoutGoog = []; + typesWithGoog.forEach(function(type) { + typesWithoutGoog.push(type.replace(/^goog\..*$/, '*')); + }); + return typesWithoutGoog; + } + function processSymbol(symbol) { addNamespaces(symbol.name.split('#')[0]); @@ -88,7 +96,7 @@ function generateExterns(typedefs, symbols, externs) { lines.push(' * @constructor'); } if (symbol.types) { - lines.push(' * @type {' + symbol.types.join('|') + '}'); + lines.push(' * @type {' + noGoogTypes(symbol.types).join('|') + '}'); } var args = []; if (symbol.params) { @@ -96,13 +104,13 @@ function generateExterns(typedefs, symbols, externs) { args.push(param.name); lines.push(' * @param {' + (param.variable ? '...' : '') + - param.types.join('|') + + noGoogTypes(param.types).join('|') + (param.optional ? '=' : '') + '} ' + param.name); }); } if (symbol.returns) { - lines.push(' * @return {' + symbol.returns.join('|') + '}'); + lines.push(' * @return {' + noGoogTypes(symbol.returns).join('|') + '}'); } if (symbol.template) { lines.push(' * @template ' + symbol.template); @@ -121,7 +129,7 @@ function generateExterns(typedefs, symbols, externs) { typedefs.forEach(function(typedef) { addNamespaces(typedef.name); lines.push('/**'); - lines.push(' * @typedef {' + typedef.types.join('|') + '}'); + lines.push(' * @typedef {' + noGoogTypes(typedef.types).join('|') + '}'); lines.push(' */'); lines.push(nameToJS(typedef.name) + ';'); lines.push('\n');