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.
This commit is contained in:
Igor Minar 2015-09-03 14:45:40 -07:00
parent 34deda594f
commit ad3b9cf232
3 changed files with 190 additions and 75 deletions

View File

@ -1,40 +1,192 @@
module.exports = { 'use strict';
signature: function(remap) { function DtsSerializer(remap) {
return function(ast) { this.remap = remap;
try { }
var text = [];
if (ast.isStatic) text.push('static '); DtsSerializer.prototype = {
text.push(ast.name);
if (ast.optional) text.push('?'); constructor: DtsSerializer,
declaration: function(buffer, ast) {
buffer.push(ast.name);
if (ast.optional) buffer.push('?');
if (ast.typeParameters) { if (ast.typeParameters) {
text.push('<'); buffer.push('<');
text.push(ast.typeParameters.join(', ')); buffer.push(ast.typeParameters.join(', '));
text.push('>'); buffer.push('>');
} }
if (ast.parameters) { if (ast.parameters) {
text.push('('); buffer.push('(');
text.push(ast.parameters.join(', ')); buffer.push(ast.parameters.join(', '));
text.push(')'); buffer.push(')');
} }
if (ast.returnType) { if (ast.returnType) {
text.push(': ', ast.returnType); buffer.push(': ', ast.returnType);
} else if (ast.parameters) { } else if (ast.parameters) {
text.push(': void'); buffer.push(': void');
} else { } else {
text.push(': any'); buffer.push(': any');
} }
var string = text.join(''); buffer.push(';\n');
for (var key in remap) { },
if (remap.hasOwnProperty(key)) {
string = string.replace(new RegExp('\\b' + key + '\\b', 'gm'), remap[key]); 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; return string;
} catch (e) { } catch (e) {
console.log(e.toString(), e.stack); console.log(e.toString(), e.stack);
return 'ERROR: ' + e.toString(); 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
};

View File

@ -42,7 +42,7 @@ module.exports = function createTypeDefinitionFile(log) {
references: def.references 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', docType: 'var',
name: exportDoc.name, name: exportDoc.name,
id: exportDoc.id, id: exportDoc.id,
heritage: ': InjectableReference' returnType: 'InjectableReference'
}); });
} }
}); });

View File

@ -8,12 +8,6 @@
{%- endmacro -%} {%- endmacro -%}
{%- macro memberInfo(signature, member) -%}
{$ commentBlock(member, 5) $}
{$ signature(member) $};
{%- endmacro -%}
// Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $} // Type definitions for Angular v{$ versionInfo.currentVersion.full | replace(r/\+/, "_") $}
// Project: http://angular.io/ // Project: http://angular.io/
// Definitions by: angular team <https://github.com/angular/> // Definitions by: angular team <https://github.com/angular/>
@ -38,38 +32,7 @@
declare module {$ module.namespace $} { declare module {$ module.namespace $} {
{%- for export in module.doc.exports -%} {%- for export in module.doc.exports -%}
{%- if export.content -%} {$ doc.dts.serializeExport(export) $}
{$ 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 %}
{% endfor %} {% endfor %}
} }