From a116b4c996aa75964bfb0009f4ad456ff0d3ec16 Mon Sep 17 00:00:00 2001 From: Noj Vek Date: Tue, 26 Jan 2016 22:59:34 -0800 Subject: [PATCH] pretty output for react jsx --- src/compiler/emitter.ts | 46 ++-- tests/baselines/reference/tsxReactEmit1.js | 6 +- tests/baselines/reference/tsxReactEmit3.js | 10 +- .../reference/tsxReactEmitNesting.js | 64 ++++++ .../reference/tsxReactEmitNesting.symbols | 139 ++++++++++++ .../reference/tsxReactEmitNesting.types | 208 ++++++++++++++++++ .../reference/tsxReactEmitWhitespace.js | 6 +- .../reference/tsxReactEmitWhitespace2.js | 16 +- .../conformance/jsx/tsxReactEmitNesting.tsx | 37 ++++ 9 files changed, 507 insertions(+), 25 deletions(-) create mode 100644 tests/baselines/reference/tsxReactEmitNesting.js create mode 100644 tests/baselines/reference/tsxReactEmitNesting.symbols create mode 100644 tests/baselines/reference/tsxReactEmitNesting.types create mode 100644 tests/cases/conformance/jsx/tsxReactEmitNesting.tsx diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 44cb0ea3ccf..86378036d65 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1257,26 +1257,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // Children if (children) { - for (let i = 0; i < children.length; i++) { - // Don't emit empty expressions - if (children[i].kind === SyntaxKind.JsxExpression && !((children[i]).expression)) { - continue; - } - - // Don't emit empty strings - if (children[i].kind === SyntaxKind.JsxText) { - const text = getTextToEmit(children[i]); - if (text !== undefined) { - write(", \""); - write(text); - write("\""); - } - } - else { + // build list of valid emittable jsx children + const emittableChildren = children.filter(isJsxChildEmittable); + if (emittableChildren.length > 0) { + // If the only child is non-jsx element, don't put it on a new line + if (emittableChildren.length == 1 && emittableChildren[0].kind !== SyntaxKind.JsxElement && emittableChildren[0].kind !== SyntaxKind.JsxSelfClosingElement) { write(", "); - emit(children[i]); + emit(emittableChildren[0]); + } + // Otherwise build a indented comma separated list + else { + increaseIndent(); + emitList(emittableChildren, 0, emittableChildren.length, /*multiLine*/ true, /*trailingComma*/ false, /*leadingComma*/ true); + decreaseIndent(); } - } } @@ -7251,7 +7245,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return result; } - function getTextToEmit(node: JsxText) { + function isJsxChildEmittable(child: JsxChild): boolean { + if (child.kind === SyntaxKind.JsxExpression) { + // Don't emit empty expressions + return !!(child).expression; + + } + else if (child.kind === SyntaxKind.JsxText) { + // Don't emit empty strings + return !!getTextToEmit(child); + } + + return true; + }; + + function getTextToEmit(node: JsxText): string { switch (compilerOptions.jsx) { case JsxEmit.React: let text = trimReactWhitespaceAndApplyEntities(node); diff --git a/tests/baselines/reference/tsxReactEmit1.js b/tests/baselines/reference/tsxReactEmit1.js index 05781ab8530..a6eac7f4824 100644 --- a/tests/baselines/reference/tsxReactEmit1.js +++ b/tests/baselines/reference/tsxReactEmit1.js @@ -70,5 +70,9 @@ var SomeClass = (function () { return SomeClass; }()); var whitespace1 = React.createElement("div", null, " "); -var whitespace2 = React.createElement("div", null, " ", p, " "); +var whitespace2 = React.createElement("div", null, + " ", + p, + " " +); var whitespace3 = React.createElement("div", null, p); diff --git a/tests/baselines/reference/tsxReactEmit3.js b/tests/baselines/reference/tsxReactEmit3.js index bd42177e187..2163a310fc8 100644 --- a/tests/baselines/reference/tsxReactEmit3.js +++ b/tests/baselines/reference/tsxReactEmit3.js @@ -8,4 +8,12 @@ declare var Foo, Bar, baz; q s ; //// [test.js] -React.createElement(Foo, null, " ", React.createElement(Bar, null, " q "), " ", React.createElement(Bar, null), " s ", React.createElement(Bar, null), React.createElement(Bar, null)); +React.createElement(Foo, null, + " ", + React.createElement(Bar, null, " q "), + " ", + React.createElement(Bar, null), + " s ", + React.createElement(Bar, null), + React.createElement(Bar, null) +); diff --git a/tests/baselines/reference/tsxReactEmitNesting.js b/tests/baselines/reference/tsxReactEmitNesting.js new file mode 100644 index 00000000000..3f12715b98c --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitNesting.js @@ -0,0 +1,64 @@ +//// [file.tsx] + +declare var vdom: any; +declare var ctrl: any; +declare var model: any; + +// A simple render function with nesting and control statements +let render = (ctrl, model) => +
+
+

todos <x>

+ +
+
+ +
    + {model.filteredTodos.map((todo) => +
  • +
    + {(!todo.editable) ? + + : null + } + + +
    +
    +
    +
    +
  • + )} +
+
+
+ + + +//// [file.js] +// A simple render function with nesting and control statements +var render = function (ctrl, model) { + return vdom.createElement("section", {class: "todoapp"}, + vdom.createElement("header", {class: "header"}, + vdom.createElement("h1", null, "todos "), + vdom.createElement("input", {class: "new-todo", autofocus: true, autocomplete: "off", placeholder: "What needs to be done?", value: model.newTodo, onKeyup: ctrl.addTodo.bind(ctrl, model)}) + ), + vdom.createElement("section", {class: "main", style: { display: (model.todos && model.todos.length) ? "block" : "none" }}, + vdom.createElement("input", {class: "toggle-all", type: "checkbox", onChange: ctrl.toggleAll.bind(ctrl)}), + vdom.createElement("ul", {class: "todo-list"}, model.filteredTodos.map(function (todo) { + return vdom.createElement("li", {class: { todo: true, completed: todo.completed, editing: todo == model.editedTodo }}, + vdom.createElement("div", {class: "view"}, + (!todo.editable) ? + vdom.createElement("input", {class: "toggle", type: "checkbox"}) + : null, + vdom.createElement("label", {onDoubleClick: function () { ctrl.editTodo(todo); }}, todo.title), + vdom.createElement("button", {class: "destroy", onClick: ctrl.removeTodo.bind(ctrl, todo)}), + vdom.createElement("div", {class: "iconBorder"}, + vdom.createElement("div", {class: "icon"}) + ) + ) + ); + })) + ) + ); +}; diff --git a/tests/baselines/reference/tsxReactEmitNesting.symbols b/tests/baselines/reference/tsxReactEmitNesting.symbols new file mode 100644 index 00000000000..efccb602747 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitNesting.symbols @@ -0,0 +1,139 @@ +=== tests/cases/conformance/jsx/file.tsx === + +declare var vdom: any; +>vdom : Symbol(vdom, Decl(file.tsx, 1, 11)) + +declare var ctrl: any; +>ctrl : Symbol(ctrl, Decl(file.tsx, 2, 11)) + +declare var model: any; +>model : Symbol(model, Decl(file.tsx, 3, 11)) + +// A simple render function with nesting and control statements +let render = (ctrl, model) => +>render : Symbol(render, Decl(file.tsx, 6, 3)) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>model : Symbol(model, Decl(file.tsx, 6, 19)) + +
+>section : Symbol(unknown) +>class : Symbol(unknown) + +
+>header : Symbol(unknown) +>class : Symbol(unknown) + +

todos <x>

+>h1 : Symbol(unknown) +>h1 : Symbol(unknown) + + +>input : Symbol(unknown) +>class : Symbol(unknown) +>autofocus : Symbol(unknown) +>autocomplete : Symbol(unknown) +>placeholder : Symbol(unknown) +>value : Symbol(unknown) +>model : Symbol(model, Decl(file.tsx, 6, 19)) +>onKeyup : Symbol(unknown) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>model : Symbol(model, Decl(file.tsx, 6, 19)) + +
+>header : Symbol(unknown) + +
+>section : Symbol(unknown) +>class : Symbol(unknown) +>style : Symbol(unknown) +>display : Symbol(display, Decl(file.tsx, 12, 38)) +>model : Symbol(model, Decl(file.tsx, 6, 19)) +>model : Symbol(model, Decl(file.tsx, 6, 19)) + + +>input : Symbol(unknown) +>class : Symbol(unknown) +>type : Symbol(unknown) +>onChange : Symbol(unknown) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) + +
    +>ul : Symbol(unknown) +>class : Symbol(unknown) + + {model.filteredTodos.map((todo) => +>model : Symbol(model, Decl(file.tsx, 6, 19)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) + +
  • +>li : Symbol(unknown) +>class : Symbol(unknown) +>todo : Symbol(todo, Decl(file.tsx, 16, 32)) +>completed : Symbol(completed, Decl(file.tsx, 16, 43)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) +>editing : Symbol(editing, Decl(file.tsx, 16, 70)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) +>model : Symbol(model, Decl(file.tsx, 6, 19)) + +
    +>div : Symbol(unknown) +>class : Symbol(unknown) + + {(!todo.editable) ? +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) + + +>input : Symbol(unknown) +>class : Symbol(unknown) +>type : Symbol(unknown) +>input : Symbol(unknown) + + : null + } + +>label : Symbol(unknown) +>onDoubleClick : Symbol(unknown) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) +>label : Symbol(unknown) + + +>button : Symbol(unknown) +>class : Symbol(unknown) +>onClick : Symbol(unknown) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>ctrl : Symbol(ctrl, Decl(file.tsx, 6, 14)) +>todo : Symbol(todo, Decl(file.tsx, 15, 42)) +>button : Symbol(unknown) + +
    +>div : Symbol(unknown) +>class : Symbol(unknown) + +
    +>div : Symbol(unknown) +>class : Symbol(unknown) + +
    +>div : Symbol(unknown) + +
    +>div : Symbol(unknown) + +
  • +>li : Symbol(unknown) + + )} +
+>ul : Symbol(unknown) + +
+>section : Symbol(unknown) + +
+>section : Symbol(unknown) + + diff --git a/tests/baselines/reference/tsxReactEmitNesting.types b/tests/baselines/reference/tsxReactEmitNesting.types new file mode 100644 index 00000000000..fff29c11525 --- /dev/null +++ b/tests/baselines/reference/tsxReactEmitNesting.types @@ -0,0 +1,208 @@ +=== tests/cases/conformance/jsx/file.tsx === + +declare var vdom: any; +>vdom : any + +declare var ctrl: any; +>ctrl : any + +declare var model: any; +>model : any + +// A simple render function with nesting and control statements +let render = (ctrl, model) => +>render : (ctrl: any, model: any) => any +>(ctrl, model) =>

todos <x>

    {model.filteredTodos.map((todo) =>
  • {(!todo.editable) ? : null }
  • )}
: (ctrl: any, model: any) => any +>ctrl : any +>model : any + +
+>

todos <x>

    {model.filteredTodos.map((todo) =>
  • {(!todo.editable) ? : null }
  • )}
: any +>section : any +>class : any + +
+>

todos <x>

: any +>header : any +>class : any + +

todos <x>

+>

todos <x>

: any +>h1 : any +>h1 : any + + +> : any +>input : any +>class : any +>autofocus : any +>autocomplete : any +>placeholder : any +>value : any +>model.newTodo : any +>model : any +>newTodo : any +>onKeyup : any +>ctrl.addTodo.bind(ctrl, model) : any +>ctrl.addTodo.bind : any +>ctrl.addTodo : any +>ctrl : any +>addTodo : any +>bind : any +>ctrl : any +>model : any + +
+>header : any + +
+>
    {model.filteredTodos.map((todo) =>
  • {(!todo.editable) ? : null }
  • )}
: any +>section : any +>class : any +>style : any +>{display:(model.todos && model.todos.length) ? "block" : "none"} : { display: string; } +>display : string +>(model.todos && model.todos.length) ? "block" : "none" : string +>(model.todos && model.todos.length) : any +>model.todos && model.todos.length : any +>model.todos : any +>model : any +>todos : any +>model.todos.length : any +>model.todos : any +>model : any +>todos : any +>length : any +>"block" : string +>"none" : string + + +> : any +>input : any +>class : any +>type : any +>onChange : any +>ctrl.toggleAll.bind(ctrl) : any +>ctrl.toggleAll.bind : any +>ctrl.toggleAll : any +>ctrl : any +>toggleAll : any +>bind : any +>ctrl : any + +
    +>
      {model.filteredTodos.map((todo) =>
    • {(!todo.editable) ? : null }
    • )}
    : any +>ul : any +>class : any + + {model.filteredTodos.map((todo) => +>model.filteredTodos.map((todo) =>
  • {(!todo.editable) ? : null }
  • ) : any +>model.filteredTodos.map : any +>model.filteredTodos : any +>model : any +>filteredTodos : any +>map : any +>(todo) =>
  • {(!todo.editable) ? : null }
  • : (todo: any) => any +>todo : any + +
  • +>
  • {(!todo.editable) ? : null }
  • : any +>li : any +>class : any +>{todo: true, completed: todo.completed, editing: todo == model.editedTodo} : { todo: boolean; completed: any; editing: boolean; } +>todo : boolean +>true : boolean +>completed : any +>todo.completed : any +>todo : any +>completed : any +>editing : boolean +>todo == model.editedTodo : boolean +>todo : any +>model.editedTodo : any +>model : any +>editedTodo : any + +
    +>
    {(!todo.editable) ? : null }
    : any +>div : any +>class : any + + {(!todo.editable) ? +>(!todo.editable) ? : null : any +>(!todo.editable) : boolean +>!todo.editable : boolean +>todo.editable : any +>todo : any +>editable : any + + +> : any +>input : any +>class : any +>type : any +>input : any + + : null +>null : null + } + +> : any +>label : any +>onDoubleClick : any +>()=>{ctrl.editTodo(todo)} : () => void +>ctrl.editTodo(todo) : any +>ctrl.editTodo : any +>ctrl : any +>editTodo : any +>todo : any +>todo.title : any +>todo : any +>title : any +>label : any + + +> : any +>button : any +>class : any +>onClick : any +>ctrl.removeTodo.bind(ctrl,todo) : any +>ctrl.removeTodo.bind : any +>ctrl.removeTodo : any +>ctrl : any +>removeTodo : any +>bind : any +>ctrl : any +>todo : any +>button : any + +
    +>
    : any +>div : any +>class : any + +
    +>
    : any +>div : any +>class : any + +
    +>div : any + +
    +>div : any + + +>li : any + + )} +
+>ul : any + +
+>section : any + +
+>section : any + + diff --git a/tests/baselines/reference/tsxReactEmitWhitespace.js b/tests/baselines/reference/tsxReactEmitWhitespace.js index 9614cbf2f2c..5db92bb33a4 100644 --- a/tests/baselines/reference/tsxReactEmitWhitespace.js +++ b/tests/baselines/reference/tsxReactEmitWhitespace.js @@ -59,7 +59,11 @@ var p = 0; // Emit " " React.createElement("div", null, " "); // Emit " ", p, " " -React.createElement("div", null, " ", p, " "); +React.createElement("div", null, + " ", + p, + " " +); // Emit only p React.createElement("div", null, p); // Emit only p diff --git a/tests/baselines/reference/tsxReactEmitWhitespace2.js b/tests/baselines/reference/tsxReactEmitWhitespace2.js index f649ca43ebd..61e7805fb3d 100644 --- a/tests/baselines/reference/tsxReactEmitWhitespace2.js +++ b/tests/baselines/reference/tsxReactEmitWhitespace2.js @@ -18,8 +18,18 @@ declare var React: any; //// [file.js] // Emit ' word' in the last string -React.createElement("div", null, "word ", React.createElement("code", null, "code"), " word"); +React.createElement("div", null, + "word ", + React.createElement("code", null, "code"), + " word" +); // Same here -React.createElement("div", null, React.createElement("code", null, "code"), " word"); +React.createElement("div", null, + React.createElement("code", null, "code"), + " word" +); // And here -React.createElement("div", null, React.createElement("code", null), " word"); +React.createElement("div", null, + React.createElement("code", null), + " word" +); diff --git a/tests/cases/conformance/jsx/tsxReactEmitNesting.tsx b/tests/cases/conformance/jsx/tsxReactEmitNesting.tsx new file mode 100644 index 00000000000..6c56ed781a6 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxReactEmitNesting.tsx @@ -0,0 +1,37 @@ +//@filename: file.tsx +//@jsx: react +//@reactNamespace: vdom + +declare var vdom: any; +declare var ctrl: any; +declare var model: any; + +// A simple render function with nesting and control statements +let render = (ctrl, model) => +
+
+

todos <x>

+ +
+
+ +
    + {model.filteredTodos.map((todo) => +
  • +
    + {(!todo.editable) ? + + : null + } + + +
    +
    +
    +
    +
  • + )} +
+
+
+