From ad3b9cf2321ef31460567c05118c40e6cdda2554 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Thu, 3 Sep 2015 14:45:40 -0700 Subject: [PATCH] fix(dts generation): rewrite the d.ts file code generator to fix bugs and apply type remap correctly Previously the type remap was not being applied to comments and free floating functions. The nunjucks template was becoming unreadable so rather than making a tweak there I rewrote it into imperative code that is much easier to follow. The output was diffed against the old output. The diff contained only the expected changes. --- .../processors/code_gen.js | 222 +++++++++++++++--- .../processors/createTypeDefinitionFile.js | 4 +- .../templates/type-definition.template.html | 39 +-- 3 files changed, 190 insertions(+), 75 deletions(-) diff --git a/docs/typescript-definition-package/processors/code_gen.js b/docs/typescript-definition-package/processors/code_gen.js index 4795aff310..1f8103cb7d 100644 --- a/docs/typescript-definition-package/processors/code_gen.js +++ b/docs/typescript-definition-package/processors/code_gen.js @@ -1,40 +1,192 @@ -module.exports = { +'use strict'; - signature: function(remap) { - return function(ast) { - try { - var text = []; - if (ast.isStatic) text.push('static '); - text.push(ast.name); - if (ast.optional) text.push('?'); - if (ast.typeParameters) { - text.push('<'); - text.push(ast.typeParameters.join(', ')); - text.push('>'); - } - if (ast.parameters) { - text.push('('); - text.push(ast.parameters.join(', ')); - text.push(')'); - } - if (ast.returnType) { - text.push(': ', ast.returnType); - } else if (ast.parameters) { - text.push(': void'); - } else { - text.push(': any'); - } - var string = text.join(''); - for (var key in remap) { - if (remap.hasOwnProperty(key)) { - string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), remap[key]); - } - } - return string; - } catch (e) { - console.log(e.toString(), e.stack); - return 'ERROR: ' + e.toString(); +function DtsSerializer(remap) { + this.remap = remap; +} + +DtsSerializer.prototype = { + + constructor: DtsSerializer, + + declaration: function(buffer, ast) { + buffer.push(ast.name); + if (ast.optional) buffer.push('?'); + if (ast.typeParameters) { + buffer.push('<'); + buffer.push(ast.typeParameters.join(', ')); + buffer.push('>'); + } + if (ast.parameters) { + buffer.push('('); + buffer.push(ast.parameters.join(', ')); + buffer.push(')'); + } + if (ast.returnType) { + buffer.push(': ', ast.returnType); + } else if (ast.parameters) { + buffer.push(': void'); + } else { + buffer.push(': any'); + } + buffer.push(';\n'); + }, + + comment: function(buffer, commentText) { + if (!(commentText && commentText.match(/\S/))) return; + + buffer.push('/**\n'); + commentText.replace(/\n*$/, '').split('\n').forEach(function(line) { + buffer.push(' * ' + line + '\n'); + }); + buffer.push(' */\n'); + }, + + member: function(buffer, ast) { + buffer.push('\n'); + this.comment(buffer, ast.content); + + if (ast.isStatic) buffer.push('static '); + this.declaration(buffer, ast); + }, + + interfaceOrClass: function(buffer, ast, isInterface) { + buffer.push(isInterface ? 'interface ' : 'class '); + buffer.push(ast.name); + buffer.push(ast.typeParams); + buffer.push(ast.heritage); + buffer.push(' {'); + buffer.indent(); + if (ast.newMember) this.member(buffer, ast.newMember); + if (ast.callMember) this.member(buffer, ast.callMember); + + ast.statics.forEach(function(staticMember) { + this.member(buffer, staticMember); + }.bind(this)); + + ast.members.forEach(function(member) { + this.member(buffer, member); + }.bind(this)); + + buffer.unindent(); + buffer.push('}'); + }, + + enum: function(buffer, ast) { + buffer.push('enum '); + buffer.push(ast.name); + buffer.push(ast.typeParams); + buffer.push(ast.heritage); + buffer.push(' {'); + buffer.indent(); + + ast.members.forEach(function(member, index) { + buffer.push('\n'); + this.comment(buffer, member.content); + buffer.push(member.name); + if (index !== (ast.members.length - 1)) { + buffer.push(',\n'); } + }.bind(this)); + + buffer.unindent(); + buffer.push('}\n'); + }, + + function: function(buffer, ast) { + buffer.push('function '); + this.declaration(buffer, ast); + }, + + var: function(buffer, ast) { + buffer.push('var '); + this.declaration(buffer, ast); + }, + + const: function(buffer, ast) { + buffer.push('const '); + this.declaration(buffer, ast); + }, + + serializeExport: function(ast) { + var buffer = new Buffer(); + buffer.push('\n'); + + try { + this.comment(buffer, ast.content); + + switch (ast.docType) { + case 'class': this.interfaceOrClass(buffer, ast, false); break; + case 'interface': this.interfaceOrClass(buffer, ast, true); break; + case 'function': this.function(buffer, ast); break; + case 'enum': this.enum(buffer, ast); break; + case 'var': this.var(buffer, ast); break; + case 'const': this.const(buffer, ast); break; + default: throw new Error("unknown docType: " + ast.docType); + } + + var string = buffer.toString(); + for (var key in this.remap) { + if (this.remap.hasOwnProperty(key)) { + string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), this.remap[key]); + } + } + + return string; + } catch (e) { + console.log(e.toString(), e.stack); + return 'ERROR: ' + e.toString(); } } }; + + +function Buffer() { + this._globalBuffer = []; + this._indentedBuffer = []; + this._indentationLevel = 1; +} + +Buffer.prototype = { + constructor: Buffer, + + push: function() { + this._indentedBuffer.push.apply(this._indentedBuffer, arguments); + }, + + indent: function() { + this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')}); + this._indentationLevel++; + this._indentedBuffer = []; + }, + + unindent: function() { + this._globalBuffer.push({indentationLevel: this._indentationLevel, content: this._indentedBuffer.join('')}); + this._indentationLevel--; + this._indentedBuffer = []; + }, + + toString: function() { + if (this._indentationLevel !== 1) { + throw new Exception("Forgot to unindent? Indentation level: " + this._indentationLevel); + } + + this.unindent(); + + var string = ''; + + this._globalBuffer.forEach(function(indentedChunk) { + var indentation = (new Array(indentedChunk.indentationLevel * 2 + 1)).join(' '); + indentedChunk.content.split('\n').forEach(function(line) { + string += indentation + line + '\n'; + }); + + }); + + return string; + } +}; + + +module.exports = { + DtsSerializer: DtsSerializer +}; diff --git a/docs/typescript-definition-package/processors/createTypeDefinitionFile.js b/docs/typescript-definition-package/processors/createTypeDefinitionFile.js index b848020ac1..58b6520ee6 100644 --- a/docs/typescript-definition-package/processors/createTypeDefinitionFile.js +++ b/docs/typescript-definition-package/processors/createTypeDefinitionFile.js @@ -42,7 +42,7 @@ module.exports = function createTypeDefinitionFile(log) { references: def.references }; }), - signature: codeGen.signature(def.remapTypes) + dts: new codeGen.DtsSerializer(def.remapTypes) }; }); @@ -82,7 +82,7 @@ module.exports = function createTypeDefinitionFile(log) { docType: 'var', name: exportDoc.name, id: exportDoc.id, - heritage: ': InjectableReference' + returnType: 'InjectableReference' }); } }); diff --git a/docs/typescript-definition-package/templates/type-definition.template.html b/docs/typescript-definition-package/templates/type-definition.template.html index b88294935c..c5a9e9214a 100644 --- a/docs/typescript-definition-package/templates/type-definition.template.html +++ b/docs/typescript-definition-package/templates/type-definition.template.html @@ -8,12 +8,6 @@ {%- endmacro -%} -{%- macro memberInfo(signature, member) -%} -{$ commentBlock(member, 5) $} - {$ signature(member) $}; -{%- endmacro -%} - - // Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $} // Project: http://angular.io/ // Definitions by: angular team @@ -38,38 +32,7 @@ declare module {$ module.namespace $} { {%- for export in module.doc.exports -%} - {%- if export.content -%} - {$ commentBlock(export, 3) $} - {%- endif %} - {$ export.docType $} {$ export.name $}{$ export.typeParams $}{%- if export.heritage == ' extends Directive' %} extends DirectiveAnnotation{% else %}{$ export.heritage $}{% endif %} - {%- if export.docType == 'class' or export.docType == 'interface' %} { - {%- if export.newMember %} - {$ memberInfo(doc.signature, export.newMember) $} - {% endif %} - {%- if export.callMember %} - {$ memberInfo(doc.signature, export.callMember) $} - {% endif -%} - {%- for static in export.statics %} - {$ memberInfo(doc.signature, static) $} - {%- endfor -%} - {%- for member in export.members %} - {$ memberInfo(doc.signature, member) $} - {%- endfor %} - } - - {%- elif export.docType == 'enum' %} { - {%- for member in export.members %} - {$ commentBlock(member, 5) $} - {$ member.name $}{% if not loop.last %}, - {%- endif -%} - {%- endfor %} - } - - {%- else -%} - {% if export.parameters %}({% for param in export.parameters %}{$ param $}{% if not loop.last %}, {% endif %}{% endfor %}){%- endif %} - {%- if export.returnType %} : {$ export.returnType $} {% endif -%} - ; - {%- endif %} + {$ doc.dts.serializeExport(export) $} {% endfor %} }