UNPKG

typescript-closure-tools

Version:

Command-line tools to convert closure-style JSDoc annotations to typescript, and to convert typescript sources to closure externs files

1,359 lines (1,321 loc) 734 kB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ "use strict"; exports.__esModule = true; function set_deep(parent, keys, value) { for (var i = 0; i < keys.length - 1; i++) parent = parent[keys[i]] || (parent[keys[i]] = {}); parent[keys[i]] = value; } /** * Combine classes, interfaces, and modules * * @param file * @returns {Symbols} */ function members(file) { var acc = { classes: {}, modules: {} }; Object.keys(file).forEach(function (name) { var symbol = file[name]; // Class and constructor if (symbol.jsdoc.tags.some(function (t) { return t.title === 'interface'; }) || symbol.jsdoc.tags.some(function (t) { return t.title === 'constructor'; })) { acc.classes[name] = { '': symbol }; } else if (name.indexOf('.prototype.') !== -1) { var parts = name.split('.prototype.'); var className = parts[0]; var memberName = parts[1]; if (acc.classes[className]) acc.classes[className][memberName] = symbol; } else { var dot = name.lastIndexOf('.'); var moduleName = name.substring(0, dot); var memberName = name.substring(dot + 1); if (!acc.modules[moduleName]) acc.modules[moduleName] = {}; acc.modules[moduleName][memberName] = symbol; } }); return acc; } exports.members = members; },{}],2:[function(require,module,exports){ "use strict"; exports.__esModule = true; var fs = require("fs"); var parser = require("./parser"); var combine = require("./combine"); var options = require("./options"); var fileByProvide = {}; var providesByFile = {}; function parent_symbol(name) { var dot = name.lastIndexOf('.'); return name.substring(0, dot); } /** * @param symbolName * @returns file that goog.provide's name */ function file(symbolName) { if (symbolName) return fileByProvide[symbolName] || file(parent_symbol(symbolName)); else return null; } exports.file = file; /** * @param fileName * @returns value of name */ function symbols(fileName) { if (!providesByFile[fileName]) providesByFile[fileName] = combine.members(parser.jsdoc(fs.readFileSync(fileName, 'utf8'))); if (providesByFile[fileName]) return providesByFile[fileName]; } exports.symbols = symbols; function symbol(symbolName) { var fileName = file(symbolName); if (fileName) { var s = symbols(fileName); return s.classes[symbolName] || s.modules[symbolName]; } else return null; } exports.symbol = symbol; function member(symbolName) { var fileName = file(symbolName); if (fileName) { var s = symbols(fileName); var dot = symbolName.lastIndexOf('.'); var moduleName = symbolName.substring(0, dot); var memberName = symbolName.substring(dot + 1); var moduleValue = s.modules[moduleName] || {}; return moduleValue[memberName] || null; } else return null; } exports.member = member; // If user gives a TSV file with provided symbols, use that if (options.provides) { fs.readFileSync(options.provides, 'utf8').split('\n').forEach(function (line) { var columns = line.split('\t'); var file = columns[0]; var symbol = columns[1]; fileByProvide[symbol] = file; }); } else { options.todo.forEach(function (todo) { var found = symbols(todo.input); var classNames = Object.keys(found.classes); var moduleNames = Object.keys(found.modules); var names = classNames.concat(moduleNames); names.forEach(function (name) { return fileByProvide[name] = todo.input; }); }); } },{"./combine":1,"./options":5,"./parser":7,"fs":9}],3:[function(require,module,exports){ "use strict"; /// <reference path="../index/doctrine.d.ts"/> /// <reference path="../index/esprima.d.ts"/> /// <reference path="../index/escodegen.d.ts"/> exports.__esModule = true; var finder = require("./finder"); var escodegen = require("escodegen"); var reserved = [ 'break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with', 'class', 'enum', 'export', 'extends', 'import', 'super' ]; function last(values) { return values[values.length - 1]; } function find(values, predicate) { for (var i = 0; i < values.length; i++) { if (predicate(values[i])) return values[i]; } return null; } function get_in(object, keys) { for (var i = 0; i < keys.length; i++) if (object instanceof Object) object = object[keys[i]]; return object; } function generate_type_name(name) { if (name === 'Array') return 'any[]'; else return name; } function generate_dictionary_type(applications) { var tKey = generate_type(applications[0]); var tValue = generate_type(applications[1]); // Typescript only allows string and number in the key position switch (tKey) { case 'string': case 'number': break; default: tKey = 'string'; } return '{ [key: ' + tKey + ']: ' + tValue + ' }'; } function generate_type_application(expression, applications) { if (expression.name === 'Array') return generate_type(applications[0]) + '[]'; else if (expression.name === 'Object') return generate_dictionary_type(applications); else return generate_applied_type(expression) + '<' + applications.map(generate_type).join(',') + '>'; } function generate_indexed_function_parameter(type, index) { return '_' + index + ': ' + generate_type(type); } function generate_function_type(params, result) { var paramString = '(' + params.map(generate_indexed_function_parameter).join(', ') + ')'; return '{ ' + paramString + ': ' + generate_type(result) + ' }'; } function comment(text) { text = text.replace(/\/\*/g, '('); text = text.replace(/\*\//g, ')'); return '/*' + text + '*/'; } /** * Generate a type, without worrying about whether generics have been applied * @param t */ function generate_applied_type(t) { switch (t.type) { case 'NameExpression': references[t.name] = true; return generate_type_name(t.name); case 'TypeApplication': return generate_type_application(t.expression, t.applications); case 'OptionalType': case 'NullableType': case 'NonNullableType': return generate_type(t.expression); case 'FunctionType': return generate_function_type(t.params, t.result); case 'RestType': return generate_type(t.expression) + '[]'; case 'UnionType': return t.elements.map(generate_type).join('|'); case 'AllLiteral': case 'NullableLiteral': return 'any'; case 'NullLiteral': return 'any /*null*/'; case 'UndefinedLiteral': return 'any /*undefined*/'; case 'VoidLiteral': return 'void'; case 'RecordType': return '{ ' + t.fields.map(generate_record_field).join('; ') + ' }'; case 'ArrayType': return generate_type(t.elements[0]) + '[]'; case 'FieldType': case 'ParameterType': default: throw new Error('Unknown type expression ' + t.type); } } function generate_type(t) { if (!t) return 'any /*missing*/'; var result = generate_applied_type(t); // If t is a name expression, look for a definition and verify templates if (t.type === 'NameExpression') { var symbol = finder.symbol(t.name); var tags = get_in(symbol, ['', 'jsdoc', 'tags']) || []; var params = []; tags.filter(function (t) { return t.title === 'template'; }) .forEach(function (tag) { tag.description.split(',').forEach(function (i) { return params.push('any'); }); }); if (params.length > 0) return t.name + '<' + params.join(', ') + '>'; } return result; } function safe_name(name) { if (reserved.indexOf(name) !== -1) return '_' + name; else return name; } function generate_function_parameter_name(annotation) { if (!annotation.type) return safe_name(annotation.name); else if (annotation.type.type === 'OptionalType') return safe_name(annotation.name) + '?'; else if (annotation.type.type === 'RestType') return '...' + safe_name(annotation.name); else return safe_name(annotation.name); } function generate_function_parameter(annotation) { return generate_function_parameter_name(annotation) + ': ' + generate_type(annotation.type); } function param_names(docs) { if (docs.value && docs.value.params) return docs.value.params.map(function (x) { return x.name; }); else return docs.jsdoc.tags.filter(function (t) { return t.title === 'param'; }).map(function (x) { return x.name; }); } function tags_by_name(tags) { var acc = {}; tags.forEach(function (tag) { acc[tag.name] = tag; }); return acc; } function generate_var(name, docs) { var typeTag = find(docs.tags, function (t) { return t.title === 'type'; }); return 'var ' + name + ': ' + generate_type(typeTag && typeTag.type) + ';'; } var VOID_TYPE = { type: { type: 'NameExpression', name: 'void' } }; function generics(docs) { var tags = docs.tags || []; var templateTags = tags.filter(function (t) { return t.title === 'template'; }); var names = templateTags.map(function (x) { return x.description; }); if (names.length === 0) return ''; else return '<' + names.join(', ') + '>'; } // TODO now that overloads are no longer needed, this should return string not string[] function generate_functions(name, value) { var docs = value.jsdoc; var names = param_names(value); var tags = docs.tags || []; var paramTags = tags.filter(function (t) { return t.title === 'param'; }); var overloads = [tags_by_name(paramTags)]; return overloads.map(function (tagsByName) { var paramStrings = generate_param_strings(names, tagsByName); var returnTag = find(tags, function (t) { return t.title === 'return' || t.title === 'returns'; }) || VOID_TYPE; return 'function ' + name + generics(docs) + '(' + paramStrings.join(', ') + '): ' + generate_type(returnTag.type) + ';'; }); } // TODO now that overloads are no longer needed, this should return string not string[] function generate_properties(name, value) { // Function if (is_function(value)) return generate_functions(name, value); else return [generate_var(name, value.jsdoc)]; } function generate_param_strings(names, types) { return names.map(function (name) { if (name in types) return generate_function_parameter(types[name]); else if (name.substring(0, 4) === 'opt_') return safe_name(name) + '?: any /* jsdoc error */'; else return safe_name(name) + ': any /* jsdoc error */'; }); } // TODO now that overloads are no longer needed, this should return string not string[] function generate_methods(name, value) { var docs = value.jsdoc; var names = param_names(value); var tags = docs.tags || []; var paramTags = tags.filter(function (t) { return t.title === 'param'; }); var overloads = [tags_by_name(paramTags)]; return overloads.map(function (tagsByName) { var paramStrings = generate_param_strings(names, tagsByName); var returnTag = find(tags, function (t) { return t.title === 'return' || t.title === 'returns'; }) || VOID_TYPE; return name + generics(docs) + '(' + paramStrings.join(', ') + '): ' + generate_type(returnTag.type); }); } function generate_field_name(name, type) { if (type && type.type === 'OptionalType') return name + '?'; else return name; } function generate_field(name, docs) { var tags = docs.tags || []; var typeTag = find(tags, function (t) { return t.title === 'type'; }); var type = typeTag && typeTag.type; var fieldName = generate_field_name(name, type); return fieldName + ': ' + generate_type(type); } function generate_record_field(field) { var fieldName = generate_field_name(field.key, field.value); return fieldName + ': ' + generate_type(field.value); } function is_function(value) { var tags = value.jsdoc.tags || []; return (value.value && value.value.type === 'FunctionExpression') || tags.some(function (t) { return t.title === 'param'; }) || tags.some(function (t) { return t.title === 'return'; }); } // TODO now that overloads are no longer needed, this should return string not string[] function generate_members(name, value) { // Function if (is_function(value)) return generate_methods(name, value); else return [generate_field(name, value.jsdoc)]; } function with_underscore(type) { var prefix = /[\w\.]+/.exec(type)[0]; var suffix = type.substring(prefix.length); return prefix + '__Class' + suffix; } function generate_class_extends(docs) { var tags = docs.tags || []; var supers = tags .filter(function (t) { return t.title === 'extends'; }) .map(function (x) { return x.type; }) .map(generate_type) .map(with_underscore); if (supers.length > 0) return 'extends ' + supers.join(', ') + ' '; else return ''; } function generate_interface_extends(docs) { var tags = docs.tags || []; var supers = tags .filter(function (t) { return t.title === 'extends'; }) .map(function (x) { return x.type; }) .map(generate_type); if (supers.length > 0) return 'extends ' + supers.join(', ') + ' '; else return ''; } function generate_implements(docs) { var supers = docs.tags .filter(function (t) { return t.title === 'implements'; }) .map(function (x) { return x.type; }) .map(generate_type); if (supers.length > 0) return 'implements ' + supers.join(', ') + ' '; else return ''; } function generate_interface(name, prototype) { var constructor = prototype['']; var acc = 'interface ' + name + generics(constructor.jsdoc) + ' ' + generate_interface_extends(constructor.jsdoc) + '{\n'; Object.keys(prototype).filter(function (name) { return name !== ''; }).forEach(function (name) { var value = prototype[name]; var text = value.originalText.replace(/\n/g, '\n '); acc += '\n'; generate_members(name, value).forEach(function (member) { acc += ' ' + text + '\n'; acc += ' ' + member + ';\n'; }); }); acc += '}'; return acc; } // TODO now that overloads are no longer needed, this should return string not string[] function generate_constructors(value) { var docs = value.jsdoc; var names = param_names(value); var paramTags = docs.tags.filter(function (t) { return t.title === 'param'; }); var overloads = [tags_by_name(paramTags)]; return overloads.map(function (tagsByName) { return 'constructor(' + generate_param_strings(names, tagsByName).join(', ') + ')'; }); } function get_type_name(tag) { if (tag.name) return tag.name; else if (tag.type) { if (tag.type.type === 'NameExpression') return tag.type.name; else if (tag.type.type === 'TypeApplication') return tag.type.expression.name; } } function generate_class(name, prototype) { var constructor = prototype['']; var acc = 'class ' + name + generics(constructor.jsdoc) + ' extends ' + name + '__Class' + generics(constructor.jsdoc) + ' { }\n'; acc += '/** Fake class which should be extended to avoid inheriting static properties */\n'; acc += 'class ' + name + '__Class' + generics(constructor.jsdoc) + ' ' + generate_class_extends(constructor.jsdoc) + generate_implements(constructor.jsdoc) + ' { \n'; var text = constructor.originalText.replace(/\n/g, '\n' + ' ' + ' '); acc += '\n'; generate_constructors(constructor).forEach(function (constructor) { acc += ' ' + ' ' + text + '\n'; acc += ' ' + ' ' + constructor + ';\n'; }); function add_members(prototype) { Object.keys(prototype).filter(function (name) { return name !== ''; }).forEach(function (name) { var value = prototype[name]; var docs = value.jsdoc; var tags = docs.tags || []; var text = value.originalText.replace(/\n/g, '\n' + ' ' + ' '); if (!tags.some(function (t) { return t.title === 'override'; })) { acc += '\n'; generate_members(name, value).forEach(function (member) { acc += ' ' + ' ' + text + '\n'; acc += ' ' + ' ' + member + ';\n'; }); } }); // Look for implemented interfaces and inject them var constructor = prototype['']; var tags = constructor.jsdoc.tags || []; var names = tags .filter(function (t) { return t.title === 'implements'; }) .map(get_type_name); var locations = names .map(finder.symbol) .filter(function (s) { return Boolean(s); }); locations.forEach(add_members); } add_members(prototype); acc += '} \n'; return acc; } function generate_enum(name, values) { function key_id(property) { if ('name' in property) return property.name; else if ('value' in property) return property.value; else throw new Error('Unknown enum property ' + property); } function safe_id(name) { if (/^\w+$/.test(name)) return name; else return "'" + name + "'"; } if (values.type === 'ObjectExpression') { var keys = values.properties .map(function (x) { return x.key; }) .map(key_id) .map(safe_id); return 'enum ' + name + ' { ' + keys.join(', ') + ' } '; } else { var reference = escodegen.generate(values); var dot = reference.lastIndexOf('.'); var moduleName = reference.substring(0, dot); var enumName = reference.substring(dot + 1); var fileName = finder.file(moduleName); if (!fileName) return 'enum ' + name + ' { /* ' + reference + ' */ } '; var symbols = finder.symbols(fileName); var moduleValue = symbols.modules[moduleName]; var enumValue = moduleValue[enumName].value; return generate_enum(name, enumValue); } } function generate_typedef(name, docs) { var tag = find(docs.tags, function (t) { return t.title === 'typedef'; }); var typedef = tag.type; switch (typedef.type) { // Skip modifiers case 'OptionalType': case 'NullableType': case 'NonNullableType': tag.type = typedef.expression; return generate_typedef(name, docs); // F function(...) becomes interface F { (...): ... } case 'FunctionType': var argumentString = typedef.params.map(generate_indexed_function_parameter).join(', '); var returnString = generate_type(typedef.result); return 'interface ' + name + ' {\n (' + argumentString + '): ' + returnString + '\n}'; // S { ... } becomes interface T { ... } case 'RecordType': var fieldsString = typedef.fields.map(function (field) { return field.key + ': ' + generate_type(field.value); }).join(';\n '); return 'interface ' + name + ' {\n ' + fieldsString + '\n}'; // T NamedType becomes interface T extends NamedType { } case 'NameExpression': references[typedef.name] = true; return 'type ' + name + ' = ' + typedef.name; // T NamedType<Param> becomes interface T extends NamedType<Param> { } case 'TypeApplication': if (typedef.expression.name === 'Object') return generate_dictionary_type(typedef.applications); else { var base = typedef.expression.name; var generics = '<' + typedef.applications.map(generate_type).join(',') + '>'; return 'interface ' + name + ' extends ' + base + generics + ' { }'; } case 'UnionType': var union = generate_type(typedef); return 'type ' + name + ' = ' + union + ';'; // Anything else becomes interface Name { /* explanation */ } default: return 'interface ' + name + ' { ' + comment(generate_type(typedef)) + ' }'; } } var references = {}; function defs(symbols) { var modules = {}; references = {}; // Generate classes Object.keys(symbols.classes).forEach(function (name) { var dot = name.lastIndexOf('.'); var moduleName = name.substring(0, dot); var propertyName = name.substring(dot + 1); var symbol = symbols.classes[name]; var constructor = symbol['']; if (!modules[moduleName]) modules[moduleName] = {}; if (constructor.jsdoc.tags.some(function (t) { return t.title === 'interface'; })) modules[moduleName][propertyName] = generate_interface(propertyName, symbol); else modules[moduleName][propertyName] = generate_class(propertyName, symbol); }); // Generate statics Object.keys(symbols.modules).forEach(function (moduleName) { var symbol = symbols.modules[moduleName]; if (!modules[moduleName]) modules[moduleName] = {}; Object.keys(symbol).forEach(function (propertyName) { var value = symbol[propertyName]; var comment = value.originalText + '\n'; if (value.jsdoc.tags.some(function (t) { return t.title === 'enum'; })) modules[moduleName][propertyName] = comment + generate_enum(propertyName, value.value); else if (value.jsdoc.tags.some(function (t) { return t.title === 'typedef'; })) modules[moduleName][propertyName] = comment + generate_typedef(propertyName, value.jsdoc); else { modules[moduleName][propertyName] = generate_properties(propertyName, value) .map(function (property) { return comment + property; }) .join('\n'); } }); }); return { modules: modules, references: Object.keys(references) }; } exports.defs = defs; },{"./finder":2,"escodegen":23}],4:[function(require,module,exports){ "use strict"; /// <reference path="../index/mkdirp.d.ts"/> /// <reference path="../index/colors.d.ts"/> exports.__esModule = true; var fs = require("fs"); var finder = require("./finder"); var generate = require("./generate"); var pretty_print = require("./pretty_print"); var options = require("./options"); var mkdirp = require("mkdirp"); require('colors'); options.todo.forEach(function (todo) { exports.currentInput = todo.input; exports.currentOutput = todo.output; try { var symbols = finder.symbols(todo.input); var defs = generate.defs(symbols); var pretty = pretty_print.pretty(defs); var parentPart = (/^.*\//).exec(todo.output) || []; var parentDir = parentPart[0] || ''; mkdirp.sync(parentDir); if (fs.existsSync(todo.output) && fs.readFileSync(todo.output, 'utf8') === pretty) { console.error('No changes\t'.green + todo.output); } else { fs.writeFileSync(todo.output, pretty); console.error('Wrote\t'.red + todo.output); } } catch (e) { console.error('ERROR\t'.bold.red + todo.input + ' ' + todo.output); throw e; } }); },{"./finder":2,"./generate":3,"./options":5,"./pretty_print":8,"colors":14,"fs":9,"mkdirp":33}],5:[function(require,module,exports){ (function (process){ "use strict"; exports.__esModule = true; var options = process.argv.slice(2); function get_option(name) { var index = options.indexOf('--' + name); if (index !== -1) { var value = options[index + 1]; options.splice(index, 2); return value; } } exports.provides = get_option('provides'); exports.globals = get_option('globals'); exports.inputRoot = get_option('input_root') || ''; exports.outputRoot = get_option('output_root') || ''; exports.includePrivate = get_option('include_private') || false; exports.todo = []; for (var i = 0; i < options.length; i += 2) { exports.todo.push({ input: options[i], output: options[i + 1] }); } }).call(this,require('_process')) },{"_process":36}],6:[function(require,module,exports){ "use strict"; exports.__esModule = true; var parser = require("./parser"); var combine = require("./combine"); var generate = require("./generate"); var pretty_print = require("./pretty_print"); function convert(text) { var docs = parser.jsdoc(text); var symbols = combine.members(docs); var out = generate.defs(symbols); var text = pretty_print.pretty(out); return text; } exports.convert = convert; window['convert'] = convert; },{"./combine":1,"./generate":3,"./parser":7,"./pretty_print":8}],7:[function(require,module,exports){ "use strict"; /// <reference path="../index/doctrine.d.ts"/> /// <reference path="../index/esprima.d.ts"/> /// <reference path="../index/escodegen.d.ts"/> exports.__esModule = true; var esprima = require("esprima"); var escodegen = require("escodegen"); var doctrine = require("doctrine"); var options = require("./options"); function values(object) { return Object.keys(object).map(function (k) { return object[k]; }); } function local_variables(tree) { var acc = []; function walk(tree) { if (!tree) { } else if (tree.type === 'FunctionDeclaration') acc.push(tree.id.name); else if (tree.type === 'VariableDeclarator') acc.push(tree.id.name); else if (tree instanceof Object) { values(tree).forEach(walk); } } walk(tree); return acc; } /** * Walk AST and extract JSDoc comments on global variables */ function extract_jsdoc(tree) { /** * Keys are global variables, values are { value, jsdoc } */ var docstrings = {}; /** * Keys are local variables, values are true or false */ var locals = {}; var requirejs = false; function not_in_locals(name) { return !locals[name]; } function add_local(name) { locals[name] = true; } function remove_local(name) { locals[name] = false; } function is_global(tree) { if (tree.type === 'Identifier') return !locals[tree.name]; else if (tree.type === 'MemberExpression' && tree.property.type === 'Identifier') return is_global(tree.object); else return false; } function is_global_assignment(tree) { return tree.type === 'ExpressionStatement' && tree.expression.type === 'AssignmentExpression' && is_global(tree.expression.left); } function is_global_declaration(tree) { return tree.type === 'ExpressionStatement' && is_global(tree.expression); } function is_this_member(tree) { return tree.type === 'MemberExpression' && tree.object.type === 'ThisExpression'; } function is_this_assignment(tree) { return tree.type === 'ExpressionStatement' && tree.expression.type === 'AssignmentExpression' && is_this_member(tree.expression.left); } function is_this_declaration(tree) { return tree.type === 'ExpressionStatement' && is_this_member(tree.expression); } function is_identifier(tree) { if (tree.type === 'Identifier') return true; else if (tree.type === 'MemberExpression' && tree.property.type === 'Identifier') return is_identifier(tree.object); else return false; } function is_assignment(tree) { return tree.type === 'ExpressionStatement' && tree.expression.type === 'AssignmentExpression' && is_identifier(tree.expression.left); } function is_declaration(tree) { return tree.type === 'ExpressionStatement' && is_identifier(tree.expression); } function is_function(tree) { return tree.type === 'FunctionDeclaration'; } function is_var(tree) { return tree.type === 'VariableDeclaration'; } /** * If we are walking inside a constructor, the expression that currently represents this */ var currentThis; /** * Look for global variables with JSDoc annotations */ function walk(tree) { // If leaf, return if (!(tree instanceof Object)) return; // If tree is a global assignment, add it to docstrings if (is_global_assignment(tree) && tree.leadingComments) { var name = escodegen.generate(tree.expression.left); tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + comment.value + '*/' }; // If tree is a constructor, remember its name as the current this if (comment.value.indexOf('@constructor') !== -1) currentThis = escodegen.generate(tree.expression.left); } }); } // If tree is a global declaration, add it to docstrings if (is_global_declaration(tree) && tree.leadingComments) { var name = escodegen.generate(tree.expression); tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + comment.value + '*/' }; } }); } // If tree is this.property = ..., add it to docstrings if (is_this_assignment(tree) && tree.leadingComments) { var memberName = escodegen.generate(tree.expression.left.property); var name = currentThis + ".prototype." + memberName; tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { var rawComment = comment.value; var removeIndent = rawComment.replace(/\n +/g, '\n '); docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + removeIndent + '*/' }; } }); } // If tree is this.property;, add it to docstrings if (is_this_declaration(tree) && tree.leadingComments) { var memberName = escodegen.generate(tree.expression.property); var name = currentThis + ".prototype." + memberName; tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { var rawComment = comment.value; var removeIndent = rawComment.replace(/\n +/g, '\n '); docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + removeIndent + '*/' }; } }); } // If tree is a local assignment and requirejs is active, add it to docstrings under MODULE if (requirejs && is_assignment(tree) && tree.leadingComments) { var name = 'MODULE.' + escodegen.generate(tree.expression.left); tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + comment.value + '*/' }; } }); } // If tree is a local declaration and requirejs is active, add it to docstrings under MODULE if (requirejs && is_declaration(tree) && tree.leadingComments) { var name = 'MODULE.' + escodegen.generate(tree.expression); tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: tree.expression.right, jsdoc: '/*' + comment.value + '*/' }; } }); } // If tree is a function declaration and requirejs is active, add it to docstrings under MODULE if (requirejs && is_function(tree) && tree.leadingComments) { var name = 'MODULE.' + tree.id.name; tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: tree, jsdoc: '/*' + comment.value + '*/' }; } }); } // If tree is var declaration and requirejs is active, add it to docstrings under MODULE if (requirejs && is_var(tree) && tree.leadingComments) { tree.declarations.forEach(function (declaration) { var name = 'MODULE.' + declaration.id.name; tree.leadingComments.forEach(function (comment) { if (comment.type === 'Block' && comment.value.charAt(0) === '*') { docstrings[name] = { value: declaration.init, jsdoc: '/*' + comment.value + '*/' }; } }); }); } // If tree is a function expression, create a new scope if (tree.type === 'FunctionExpression' || tree.type === 'FunctionDeclaration') { var params = tree.params.map(function (p) { return p.name; }); var vars = local_variables(tree); var introduced = vars.concat(params).filter(not_in_locals); // Add new local variables introduced.forEach(add_local); // Walk children values(tree.body).forEach(walk); // Remove local variables introduced.forEach(remove_local); } else if (tree.type === 'CallExpression' && tree.callee.name === 'define' && !locals['define']) { requirejs = true; values(tree).forEach(walk); } else { values(tree).forEach(walk); } } // function is_assign_member(tree) { // return tree && // tree.type === 'ExpressionStatement' && // tree.expression.type === 'AssignmentExpression' && // tree.expression.left.type === 'MemberExpression'; // } // // function is_assign_module(tree) { // return is_assign_member(tree) && // tree.expression.left.object.name === 'module' && // tree.expression.left.property.name === 'exports' && // tree.expression.right.type === 'Identifier'; // } // // function is_assign_exports(tree) { // return is_assign_member(tree) && // tree.expression.left.object.name === 'exports' && // tree.expression.right.type === 'Identifier'; // } // // /** // * Look for exports of the form // * exports.$name = $var // * module.exports = $var // */ // function find_exports(tree) { // var exports = {}; // // function dfs(tree) { // if (!(tree instanceof Object)) { } // // module.exports = ... // else if (is_assign_module(tree)) { // var localName = tree.expression.right.name; // // exports['EXPORTS'] = docstrings[localName]; // } // // exports.$var = ... // else if (is_assign_exports(tree)){ // var exportName = tree.expression.left.property; // var localName = tree.expression.right.name; // // exports[exportName] = docstrings[localName]; // } // else { // values(tree).forEach(dfs); // } // } // // dfs(tree.body[21]); // // return { // 'MODULE': exports // }; // } // /** // * Keys are local variables, values are { value, jsdoc } // */ // var localDocstrings = {}; // // /** // * Look for local variables with JSDoc annotations, that are exported with: // * exports = $var // * exports.$name = $var // * module.exports = $var // * module.exports.$name = var // */ // function walk_requirejs(tree) { // // If leaf, return // if (!(tree instanceof Object)) // return; // // // If tree is a global assignment, add it to docstrings // if (is_assignment(tree)) { // var name = escodegen.generate(tree.expression.left); // var comments = tree.leadingComments || []; // comments.forEach(comment => { // if (comment.type === 'Block' && comment.value.charAt(0) === '*') { // localDocstrings[name] = { // value: tree.expression.right, // jsdoc: '/*' + comment.value + '*/' // }; // } // }); // } // // // If tree is a global declaration, add it to docstrings // if (is_declaration(tree)) { // name = escodegen.generate(tree.expression); // comments = tree.leadingComments || []; // comments.forEach(comment => { // if (comment.type === 'Block' && comment.value.charAt(0) === '*') { // localDocstrings[name] = { // value: tree.expression.right, // jsdoc: '/*' + comment.value + '*/' // }; // } // }); // } // // // If tree is a function expression, create a new scope // if (tree.type === 'FunctionExpression' || tree.type === 'FunctionDeclaration') { // var params = tree.params.map(p => p.name); // var vars = local_variables(tree); // var introduced = vars.concat(params).filter(not_in_locals); // // // Add new local variables // introduced.forEach(add_local); // // Walk children // values(tree.body).forEach(walk); // // Remove local variables // introduced.forEach(remove_local); // } // // Recognize define(function(require, module, exports) { ... }) // else if (tree.type === 'CallExpression' && tree.callee.name === 'define' && !locals['define']) { // walk_requirejs(tree); // } // // If tree has children, walk them // else { // values(tree).forEach(walk); // } // } walk(tree); return docstrings; } function parse_comments(comments) { var parsed = {}; Object.keys(comments).forEach(function (name) { var jsdoc = comments[name].jsdoc; var value = comments[name].value; var parsedDoc = doctrine.parse(jsdoc, { unwrap: true }); parsed[name] = { value: value, jsdoc: parsedDoc, originalText: jsdoc }; }); return parsed; } function remove_private(parsed) { // Identify all private names var privatePrefixes = []; Object.keys(parsed).forEach(function (name) { var jsdoc = parsed[name].jsdoc; var text = parsed[name].originalText; var isPrivate = text.indexOf('@private') !== -1; var isType = jsdoc.tags.some(function (t) { return t.title === 'typedef' || t.title === 'interface' || t.title === 'constructor' || t.title === 'enum'; }); if (isPrivate && !isType) { privatePrefixes.push(name); } }); // Identify all public names var publicNames = Object.keys(parsed).filter(function (name) { return !privatePrefixes.some(function (privateName) { return name.substring(0, privateName.length) === privateName; }); }); // Filter out just public names var acc = {}; publicNames.forEach(function (name) { acc[name] = parsed[name]; }); return acc; } /** * @param code * @returns Parsed file */ function jsdoc(code) { var tree = esprima.parse(code, { attachComment: true }); var comments = extract_jsdoc(tree.body); var parsed = parse_comments(comments); return options.includePrivate ? parsed : remove_private(parsed); } exports.jsdoc = jsdoc; },{"./options":5,"doctrine":22,"escodegen":23,"esprima":25}],8:[function(require,module,exports){ "use strict"; exports.__esModule = true; var main = require("./main"); var finder = require("./finder"); var options = require("./options"); function common_prefix(x, y) { var n = 0; while (n < x.length && n < y.length && x.charAt(n) === y.charAt(n)) n++; while (n > 0 && x.charAt(n - 1) !== '/') n--; return x.substring(0, n); } function drop_prefix(value, prefix) { if (value.substring(0, prefix.length) === prefix) return value.substring(prefix.length); else return value; } function unique(values) { var acc = {}; values.forEach(function (name) { return acc[name] = true; }); return Object.keys(acc); } function pretty(out) { var acc = ''; function emit_reference(filePath) { if (filePath && filePath !== main.currentInput) { var reference = drop_prefix(filePath, options.inputRoot); var output = drop_prefix(main.currentOutput, options.outputRoot); var common = common_prefix(reference, output); reference = drop_prefix(reference, common); output = drop_prefix(output, common); var goUp = output .split('/') .slice(1) .map(function (_) { return '..'; }) .join('/'); if (goUp === '') goUp = '.'; acc += '/// <reference path="' + goUp + '/' + reference + '" />\n'; } } function change_extension(filePath) { if (filePath && filePath.substring(filePath.length - 3) === '.js') return filePath.substring(0, filePath.length - 3) + '.d.ts'; else return filePath; } if (options.globals) emit_reference(options.globals); var files = out.references .map(finder.file) .filter(function (file) { return file !== null; }); unique(files).filter(function (file) { return file !== main.currentInput; }) .map(change_extension) .forEach(emit_reference); // TODO substitute goog.string // TODO break static imports Object.keys(out.modules).forEach(function (moduleName) { // TODO eliminate when https://typescript.codeplex.com/workitem/2490 is resolved var safeName = moduleName.replace(/\bstring\b/, '_string'); var moduleValue = out.modules[moduleName]; acc += '\ndeclare module ' + safeName + ' {\n'; Object.keys(moduleValue).forEach(function (propertyName) { var member = moduleValue[propertyName]; member = member.replace(/\n/g, '\n '); acc += '\n ' + member + '\n'; }); acc += '}\n'; }); return acc; } exports.pretty = pretty; },{"./finder":2,"./main":4,"./options":5}],9:[function(require,module,exports){ },{}],10:[function(require,module,exports){ /* The MIT License (MIT) Original Library - Copyright (c) Marak Squires Additional functionality - Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var colors = {}; module['exports'] = colors; colors.themes = {}; var util = require('util'); var ansiStyles = colors.styles = require('./styles'); var defineProps = Object.defineProperties; var newLineRegex = new RegExp(/[\r\n]+/g); colors.supportsColor = require('./system/supports-colors').supportsColor; if (typeof colors.enabled === 'undefined') { colors.enabled = colors.supportsColor() !== false; } colors.enable = function() { colors.enabled = true; }; colors.disable = function() { colors.enabled = false; }; colors.stripColors = colors.strip = function(str) { return ('' + str).replace(/\x1B\[\d+m/g, ''); }; // eslint-disable-next-line no-unused-vars var stylize = colors.stylize = function stylize(str, style) { if (!colors.enabled) { return str+''; } return ansiStyles[style].open + str + ansiStyles[style].close; }; var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; var escapeStringRegexp = function(str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); }; function build(_styles) { var builder = function builder() { return applyStyle.apply(builder, arguments); }; builder._styles = _styles; // __proto__ is used because we must return a function, but there is // no way to create a function with a different prototype. builder.__proto__ = proto; return builder; } var styles = (function() { var ret = {}; ansiStyles.grey = ansiStyles.gray; Object.keys(ansiStyles).forEach(function(key) { ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); ret[key] = { get: function() { return build(this._styles.concat(key)); }, }; }); return ret; })(); var proto = defineProps(function colors() {}, styles); function applyStyle() { var args = Array.prototype.slice.call(arguments); var str = args.map(function(arg) { if (arg != undefined && arg.constructor === String) { return arg; } else { return util.inspect(arg); } }).join(' '); if (!colors.enabled || !str) { return str; } var newLinesPresent = str.indexOf('\n') != -1; var nestedStyles = this._styles; var i = nestedStyles.length; while (i--) { var code = ansiStyles[nestedStyles[i]]; str = code.open + str.replace(code.closeRe, code.open) + code.close; if (newLinesPresent) { str = str.replace(newLineRegex, function(match) { return code.close + match + code.open; }); } } return str; } colors.setTheme = function(theme) { if (typeof theme === 'string') { console.log('colors.setTheme now only accepts an object, not a string. ' + 'If you are trying to set a theme from a file, it is now your (the ' + 'caller\'s) responsibility to require the file. The old syntax ' + 'looked like colors.setTheme(__dirname + ' + '\'/../themes/generic-logging.js\'); The new syntax looks like '+ 'colors.setTheme(require(__dirname + ' + '\'/../themes/generic-logging.js\'));'); re