Merge pull request #7919 from Microsoft/transforms-fix7878

[Transforms] Revert to old emit for metadata.
This commit is contained in:
Ron Buckton
2016-04-07 10:16:53 -07:00
22 changed files with 318 additions and 290 deletions

View File

@@ -854,14 +854,12 @@ namespace ts {
);
}
export function createMetadataHelper(metadataKey: string, metadataValue: Expression, defer?: boolean) {
export function createMetadataHelper(metadataKey: string, metadataValue: Expression) {
return createCall(
createIdentifier("__metadata"),
[
createLiteral(metadataKey),
defer
? createArrowFunction([], metadataValue)
: metadataValue
metadataValue
]
);
}

View File

@@ -6,6 +6,11 @@
namespace ts {
type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration;
/**
* Indicates whether to emit type metadata in the new format.
*/
const USE_NEW_TYPE_METADATA_FORMAT = false;
const enum TypeScriptSubstitutionFlags {
/** Enables substitutions for decorated classes. */
DecoratedClasses = 1 << 0,
@@ -1354,6 +1359,30 @@ namespace ts {
* @param decoratorExpressions The destination array to which to add new decorator expressions.
*/
function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (USE_NEW_TYPE_METADATA_FORMAT) {
addNewTypeMetadata(node, decoratorExpressions);
}
else {
addOldTypeMetadata(node, decoratorExpressions);
}
}
function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
let properties: ObjectLiteralElement[];
if (shouldAddTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node)));
}
if (shouldAddParamTypesMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node)));
}
if (shouldAddReturnTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node)));
}
}
}
function addNewTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
let properties: ObjectLiteralElement[];
if (shouldAddTypeMetadata(node)) {
@@ -1366,7 +1395,7 @@ namespace ts {
(properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction([], serializeReturnTypeOfNode(node))));
}
if (properties) {
decoratorExpressions.push(createMetadataHelper("design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true), /*defer*/ false));
decoratorExpressions.push(createMetadataHelper("design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true)));
}
}
}

View File

@@ -49,15 +49,11 @@ var MyComponent = (function () {
}());
__decorate([
decorator,
__metadata("design:typeinfo", {
type: function () { return Function; },
paramTypes: function () { return [Object]; },
returnType: function () { return void 0; }
})
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], MyComponent.prototype, "method", null);
MyComponent = __decorate([
decorator,
__metadata("design:typeinfo", {
paramTypes: function () { return [service_1.default]; }
})
__metadata("design:paramtypes", [service_1.default])
], MyComponent);

View File

@@ -24,9 +24,7 @@ var MyClass = (function () {
}());
__decorate([
decorator,
__metadata("design:typeinfo", {
type: function () { return Function; },
paramTypes: function () { return []; },
returnType: function () { return void 0; }
})
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], MyClass.prototype, "doSomething", null);

View File

@@ -0,0 +1,3 @@
"use strict";
var x = 0;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,7 @@
file.ts(1,3): error TS1005: ';' expected.
==== file.ts (1 errors) ====
a b
~
!!! error TS1005: ';' expected.

View File

@@ -0,0 +1,4 @@
"use strict";
a;
b;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,5 @@
define(["require", "exports"], function (require, exports) {
"use strict";
var x = 0;
});
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,4 @@
"use strict";
/// <reference path="file2.ts" />
var x = 0;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,2 @@
"use strict";
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var x = 0;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var x = 0;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,5 @@
define(["require", "exports", "SomeOtherName"], function (require, exports, SomeName_1) {
"use strict";
use(SomeName_1.foo);
});
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,16 @@
System.register(["SomeOtherName"], function (exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var SomeName_1;
return {
setters: [
function (SomeName_1_1) {
SomeName_1 = SomeName_1_1;
}
],
execute: function () {
use(SomeName_1.foo);
}
};
});
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,13 @@
(function (dependencies, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define(dependencies, factory);
}
})(["require", "exports", "SomeOtherName"], function (require, exports) {
"use strict";
var SomeName_1 = require("SomeOtherName");
use(SomeName_1.foo);
});
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,12 @@
System.register("NamedModule", [], function (exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var x;
return {
setters: [],
execute: function () {
x = 1;
}
};
});
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var x;
//# sourceMappingURL=b.js.map

View File

@@ -0,0 +1,19 @@
"use strict";
var db_1 = require('./db');
function someDecorator(target) {
return target;
}
var MyClass = (function () {
function MyClass(db) {
this.db = db;
this.db.doSomething();
}
return MyClass;
}());
MyClass = __decorate([
someDecorator,
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" && _a || Object])
], MyClass);
exports.MyClass = MyClass;
var _a;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var x = 0;
//# sourceMappingURL=file.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var a = 10;
//# sourceMappingURL=input.js.map

View File

@@ -0,0 +1,3 @@
"use strict";
var x = React.createElement("div", null);
//# sourceMappingURL=file.js.map

View File

@@ -5,296 +5,195 @@ module ts {
interface TranspileTestSettings {
options?: TranspileOptions;
expectedOutput?: string;
expectedDiagnosticCodes?: number[];
}
function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) {
if(!expectedDiagnosticCodes) {
return;
}
function transpilesCorrectly(name: string, input: string, testSettings: TranspileTestSettings) {
describe(name, () => {
let justName: string;
let transpileOptions: TranspileOptions;
let canUseOldTranspile: boolean;
let toBeCompiled: Harness.Compiler.TestFile[];
let transpileResult: TranspileOutput;
let oldTranspileResult: string;
let oldTranspileDiagnostics: Diagnostic[];
for (let i = 0; i < expectedDiagnosticCodes.length; i++) {
assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`);
}
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
}
before(() => {
transpileOptions = testSettings.options || {};
if (!transpileOptions.compilerOptions) {
transpileOptions.compilerOptions = {};
}
function test(input: string, testSettings: TranspileTestSettings): void {
if (transpileOptions.compilerOptions.newLine === undefined) {
// use \r\n as default new line
transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
}
let transpileOptions: TranspileOptions = testSettings.options || {};
if (!transpileOptions.compilerOptions) {
transpileOptions.compilerOptions = {};
}
if(transpileOptions.compilerOptions.newLine === undefined) {
// use \r\n as default new line
transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed;
}
transpileOptions.compilerOptions.sourceMap = true;
let canUseOldTranspile = !transpileOptions.renamedDependencies;
if (!transpileOptions.fileName) {
transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts";
}
transpileOptions.reportDiagnostics = true;
let transpileModuleResult = transpileModule(input, transpileOptions);
transpileOptions.reportDiagnostics = true;
checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes);
justName = "transpile/" + name.replace(/[^a-z0-9\-. ]/ig, "") + (transpileOptions.compilerOptions.jsx ? ".tsx" : ".ts");
toBeCompiled = [{
unitName: transpileOptions.fileName,
content: input
}];
if (testSettings.expectedOutput !== undefined) {
assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput);
}
canUseOldTranspile = !transpileOptions.renamedDependencies;
transpileResult = transpileModule(input, transpileOptions);
if (canUseOldTranspile) {
let diagnostics: Diagnostic[] = [];
let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName);
checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes);
if (testSettings.expectedOutput) {
assert.equal(transpileResult, testSettings.expectedOutput);
}
}
// check source maps
if (!transpileOptions.compilerOptions) {
transpileOptions.compilerOptions = {};
}
if (!transpileOptions.fileName) {
transpileOptions.fileName = transpileOptions.compilerOptions.jsx ? "file.tsx" : "file.ts";
}
transpileOptions.compilerOptions.sourceMap = true;
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
let expectedSourceMapFileName = removeFileExtension(getBaseFileName(normalizeSlashes(transpileOptions.fileName))) + ".js.map";
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
if (testSettings.expectedOutput !== undefined) {
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
}
else {
// expected output is not set, just verify that output text has sourceMappingURL as a last line
let output = transpileModuleResultWithSourceMap.outputText;
assert.isTrue(output.length >= expectedSourceMappingUrlLine.length);
if (output.length === expectedSourceMappingUrlLine.length) {
assert.equal(output, expectedSourceMappingUrlLine);
}
else {
let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine
assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1);
}
}
}
it("Generates no diagnostics with valid inputs", () => {
// No errors
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates no diagnostics for missing file references", () => {
test(`/// <reference path="file2.ts" />
var x = 0;`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates no diagnostics for missing module imports", () => {
test(`import {a} from "module2";`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates expected syntactic diagnostics", () => {
test(`a b`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } }, expectedDiagnosticCodes: [1005] }); /// 1005: ';' Expected
});
it("Does not generate semantic diagnostics", () => {
test(`var x: string = 0;`,
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
});
it("Generates module output", () => {
test(`var x = 0;`,
{
options: { compilerOptions: { module: ModuleKind.AMD } },
expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n "use strict";\r\n var x = 0;\r\n});\r\n`
if (canUseOldTranspile) {
oldTranspileDiagnostics = [];
oldTranspileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, oldTranspileDiagnostics, transpileOptions.moduleName);
}
});
});
it("Uses correct newLine character", () => {
test(`var x = 0;`,
{
options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } },
expectedOutput: `"use strict";\nvar x = 0;\n`
after(() => {
justName = undefined;
transpileOptions = undefined;
canUseOldTranspile = undefined;
toBeCompiled = undefined;
transpileResult = undefined;
oldTranspileResult = undefined;
oldTranspileDiagnostics = undefined;
});
});
it("Sets module name", () => {
let output =
`System.register("NamedModule", [], function (exports_1, context_1) {\n` +
` "use strict";\n` +
` var __moduleName = context_1 && context_1.id;\n` +
` var x;\n` +
` return {\n` +
` setters: [],\n` +
` execute: function () {\n` +
` x = 1;\n` +
` }\n` +
` };\n` +
`});\n`;
test("var x = 1;",
{
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" },
expectedOutput: output
})
});
it("No extra errors for file without extension", () => {
test(`"use strict";\r\nvar x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } });
});
it("Rename dependencies - System", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`System.register(["SomeOtherName"], function (exports_1, context_1) {\n` +
` "use strict";\n` +
` var __moduleName = context_1 && context_1.id;\n` +
` var SomeName_1;\n` +
` return {\n` +
` setters: [\n` +
` function (SomeName_1_1) {\n` +
` SomeName_1 = SomeName_1_1;\n` +
` }\n` +
` ],\n` +
` execute: function () {\n` +
` use(SomeName_1.foo);\n` +
` }\n` +
` };\n` +
`});\n`
test(input,
{
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Rename dependencies - AMD", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`define(["require", "exports", "SomeOtherName"], function (require, exports, SomeName_1) {\n` +
` "use strict";\n` +
` use(SomeName_1.foo);\n` +
`});\n`;
test(input,
{
options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Rename dependencies - UMD", () => {
let input =
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`
let output =
`(function (dependencies, factory) {\n` +
` if (typeof module === 'object' && typeof module.exports === 'object') {\n` +
` var v = factory(require, exports); if (v !== undefined) module.exports = v;\n` +
` }\n` +
` else if (typeof define === 'function' && define.amd) {\n` +
` define(dependencies, factory);\n` +
` }\n` +
`})(["require", "exports", "SomeOtherName"], function (require, exports) {\n` +
` "use strict";\n` +
` var SomeName_1 = require("SomeOtherName");\n` +
` use(SomeName_1.foo);\n` +
`});\n`
test(input,
{
options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } },
expectedOutput: output
});
});
it("Transpile with emit decorators and emit metadata", () => {
let input =
`import {db} from './db';\n` +
`function someDecorator(target) {\n` +
` return target;\n` +
`} \n` +
`@someDecorator\n` +
`class MyClass {\n` +
` db: db;\n` +
` constructor(db: db) {\n` +
` this.db = db;\n` +
` this.db.doSomething(); \n` +
` }\n` +
`}\n` +
`export {MyClass}; \n`
let output =
`"use strict";\n` +
`var db_1 = require(\'./db\');\n` +
`function someDecorator(target) {\n` +
` return target;\n` +
`}\n` +
`var MyClass = (function () {\n` +
` function MyClass(db) {\n` +
` this.db = db;\n` +
` this.db.doSomething();\n` +
` }\n` +
` return MyClass;\n` +
`}());\n` +
`MyClass = __decorate([\n` +
` someDecorator, \n` +
` __metadata(\'design:paramtypes\', [(typeof (_a = typeof db_1.db !== \'undefined\' && db_1.db) === \'function\' && _a) || Object])\n` +
`], MyClass);\n` +
`exports.MyClass = MyClass;\n` +
`var _a;\n`;
test(input,
{
options: {
compilerOptions: {
module: ModuleKind.CommonJS,
newLine: NewLineKind.LineFeed,
noEmitHelpers: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ScriptTarget.ES5,
it("Correct errors for " + justName, () => {
Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".errors.txt"), () => {
if (transpileResult.diagnostics.length === 0) {
return null;
}
},
expectedOutput: output
return Harness.Compiler.getErrorBaseline(toBeCompiled, transpileResult.diagnostics);
});
});
});
it("Supports backslashes in file name", () => {
test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "a\\b.ts" }});
});
if (canUseOldTranspile) {
it("Correct errors (old transpile) for " + justName, () => {
Harness.Baseline.runBaseline("Correct errors", justName.replace(/\.tsx?$/, ".oldTranspile.errors.txt"), () => {
if (oldTranspileDiagnostics.length === 0) {
return null;
}
it("transpile file as 'tsx' if 'jsx' is specified", () => {
let input = `var x = <div/>`;
let output = `"use strict";\nvar x = React.createElement("div", null);\n`;
test(input, {
expectedOutput: output,
options: { compilerOptions: { jsx: JsxEmit.React, newLine: NewLineKind.LineFeed } }
})
});
it("transpile .js files", () => {
const input = "const a = 10;";
const output = `"use strict";\nvar a = 10;\n`;
test(input, {
expectedOutput: output,
options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true },
expectedDiagnosticCodes: []
return Harness.Compiler.getErrorBaseline(toBeCompiled, oldTranspileDiagnostics);
});
});
}
it("Correct output for " + justName, () => {
Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".js"), () => {
return transpileResult.outputText;
});
});
if (canUseOldTranspile) {
it("Correct output (old transpile) for " + justName, () => {
Harness.Baseline.runBaseline("Correct output", justName.replace(/\.tsx?$/, ".oldTranspile.js"), () => {
return oldTranspileResult;
});
});
}
});
})
}
transpilesCorrectly("Generates no diagnostics with valid inputs", `var x = 0;`, {
options: { compilerOptions: { module: ModuleKind.CommonJS } }
});
transpilesCorrectly("Generates no diagnostics for missing file references", `/// <reference path="file2.ts" />
var x = 0;`, {
options: { compilerOptions: { module: ModuleKind.CommonJS } }
});
transpilesCorrectly("Generates no diagnostics for missing module imports", `import {a} from "module2";`, {
options: { compilerOptions: { module: ModuleKind.CommonJS } }
});
transpilesCorrectly("Generates expected syntactic diagnostics", `a b`, {
options: { compilerOptions: { module: ModuleKind.CommonJS } }
});
transpilesCorrectly("Does not generate semantic diagnostics", `var x: string = 0;`, {
options: { compilerOptions: { module: ModuleKind.CommonJS } }
});
transpilesCorrectly("Generates module output", `var x = 0;`, {
options: { compilerOptions: { module: ModuleKind.AMD } }
});
transpilesCorrectly("Uses correct newLine character", `var x = 0;`, {
options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } }
});
transpilesCorrectly("Sets module name", "var x = 1;", {
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" }
});
transpilesCorrectly("No extra errors for file without extension", `"use strict";\r\nvar x = 0;`, {
options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" }
});
transpilesCorrectly("Rename dependencies - System",
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
});
transpilesCorrectly("Rename dependencies - AMD",
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
options: { compilerOptions: { module: ModuleKind.AMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
});
transpilesCorrectly("Rename dependencies - UMD",
`import {foo} from "SomeName";\n` +
`declare function use(a: any);\n` +
`use(foo);`, {
options: { compilerOptions: { module: ModuleKind.UMD, newLine: NewLineKind.LineFeed }, renamedDependencies: { "SomeName": "SomeOtherName" } }
});
transpilesCorrectly("Transpile with emit decorators and emit metadata",
`import {db} from './db';\n` +
`function someDecorator(target) {\n` +
` return target;\n` +
`} \n` +
`@someDecorator\n` +
`class MyClass {\n` +
` db: db;\n` +
` constructor(db: db) {\n` +
` this.db = db;\n` +
` this.db.doSomething(); \n` +
` }\n` +
`}\n` +
`export {MyClass}; \n`, {
options: {
compilerOptions: {
module: ModuleKind.CommonJS,
newLine: NewLineKind.LineFeed,
noEmitHelpers: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
target: ScriptTarget.ES5,
}
}
});
transpilesCorrectly("Supports backslashes in file name", "var x", {
options: { fileName: "a\\b.ts" }
});
transpilesCorrectly("transpile file as 'tsx' if 'jsx' is specified", `var x = <div/>`, {
options: { compilerOptions: { jsx: JsxEmit.React, newLine: NewLineKind.LineFeed } }
});
transpilesCorrectly("transpile .js files", "const a = 10;", {
options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true }
});
});
}