|
725 | 725 | return this;
|
726 | 726 | };
|
727 | 727 |
|
| 728 | + /** |
| 729 | + * Inserts a new chunk that can be used to asynchronously render or write to it |
| 730 | + * @param callback {Function} The function that will be called with the new chunk |
| 731 | + * @returns {Chunk} A copy of this chunk instance in order to further chain function calls on the chunk |
| 732 | + */ |
728 | 733 | Chunk.prototype.map = function(callback) {
|
729 | 734 | var cursor = new Chunk(this.root, this.next, this.taps),
|
730 | 735 | branch = new Chunk(this.root, cursor, this.taps);
|
|
740 | 745 | return cursor;
|
741 | 746 | };
|
742 | 747 |
|
| 748 | + /** |
| 749 | + * Like Chunk#map but additionally resolves a thenable. If the thenable succeeds the callback is invoked with |
| 750 | + * a new chunk that can be used to asynchronously render or write to it, otherwise if the thenable is rejected |
| 751 | + * then the error body is rendered if available, an error is logged, and the callback is never invoked. |
| 752 | + * @param {Chunk} The current chunk to insert a new chunk |
| 753 | + * @param thenable {Thenable} the target thenable to await |
| 754 | + * @param context {Context} context to use to render the deferred chunk |
| 755 | + * @param bodies {Object} may optionally contain an "error" for when the thenable is rejected |
| 756 | + * @param callback {Function} The function that will be called with the new chunk |
| 757 | + * @returns {Chunk} A copy of this chunk instance in order to further chain function calls on the chunk |
| 758 | + */ |
| 759 | + function mapThenable(chunk, thenable, context, bodies, callback) { |
| 760 | + return chunk.map(function(asyncChunk) { |
| 761 | + thenable.then(function(data) { |
| 762 | + try { |
| 763 | + callback(asyncChunk, data); |
| 764 | + } catch (err) { |
| 765 | + // handle errors the same way Chunk#map would. This logic is only here since the thenable defers |
| 766 | + // logic such that the try / catch in Chunk#map would not capture it. |
| 767 | + dust.log(err, ERROR); |
| 768 | + asyncChunk.setError(err); |
| 769 | + } |
| 770 | + }, function(err) { |
| 771 | + dust.log('Unhandled promise rejection in `' + context.getTemplateName() + '`', INFO); |
| 772 | + asyncChunk.renderError(err, context, bodies).end(); |
| 773 | + }); |
| 774 | + }); |
| 775 | + } |
| 776 | + |
743 | 777 | Chunk.prototype.tap = function(tap) {
|
744 | 778 | var taps = this.taps;
|
745 | 779 |
|
|
861 | 895 | var body = bodies.block,
|
862 | 896 | skip = bodies['else'];
|
863 | 897 |
|
| 898 | + if (dust.isThenable(elem)) { |
| 899 | + return mapThenable(this, elem, context, bodies, function(chunk, data) { |
| 900 | + chunk.exists(data, context, bodies).end(); |
| 901 | + }); |
| 902 | + } |
| 903 | + |
864 | 904 | if (!dust.isEmpty(elem)) {
|
865 | 905 | if (body) {
|
866 | 906 | return body(this, context);
|
|
876 | 916 | var body = bodies.block,
|
877 | 917 | skip = bodies['else'];
|
878 | 918 |
|
| 919 | + if (dust.isThenable(elem)) { |
| 920 | + return mapThenable(this, elem, context, bodies, function(chunk, data) { |
| 921 | + chunk.notexists(data, context, bodies).end(); |
| 922 | + }); |
| 923 | + } |
| 924 | + |
879 | 925 | if (dust.isEmpty(elem)) {
|
880 | 926 | if (body) {
|
881 | 927 | return body(this, context);
|
|
970 | 1016 | * @return {Chunk}
|
971 | 1017 | */
|
972 | 1018 | Chunk.prototype.await = function(thenable, context, bodies, auto, filters) {
|
973 |
| - return this.map(function(chunk) { |
974 |
| - thenable.then(function(data) { |
975 |
| - if (bodies) { |
976 |
| - chunk = chunk.section(data, context, bodies); |
977 |
| - } else { |
978 |
| - // Actually a reference. Self-closing sections don't render |
979 |
| - chunk = chunk.reference(data, context, auto, filters); |
980 |
| - } |
981 |
| - chunk.end(); |
982 |
| - }, function(err) { |
983 |
| - var errorBody = bodies && bodies.error; |
984 |
| - if(errorBody) { |
985 |
| - chunk.render(errorBody, context.push(err)).end(); |
986 |
| - } else { |
987 |
| - dust.log('Unhandled promise rejection in `' + context.getTemplateName() + '`', INFO); |
988 |
| - chunk.end(); |
989 |
| - } |
990 |
| - }); |
| 1019 | + return mapThenable(this, thenable, context, bodies, function(chunk, data) { |
| 1020 | + if (bodies) { |
| 1021 | + chunk.section(data, context, bodies).end(); |
| 1022 | + } else { |
| 1023 | + // Actually a reference. Self-closing sections don't render |
| 1024 | + chunk.reference(data, context, auto, filters).end(); |
| 1025 | + } |
991 | 1026 | });
|
992 | 1027 | };
|
993 | 1028 |
|
| 1029 | + /** |
| 1030 | + * Render an error body if available |
| 1031 | + * @param err {Error} error that occurred |
| 1032 | + * @param context {Context} context to use to render the error |
| 1033 | + * @param bodies {Object} may optionally contain an "error" which will be rendered |
| 1034 | + * @return {Chunk} |
| 1035 | + */ |
| 1036 | + Chunk.prototype.renderError = function(err, context, bodies) { |
| 1037 | + var errorBody = bodies && bodies.error; |
| 1038 | + if (errorBody) { |
| 1039 | + return this.render(errorBody, context.push(err)); |
| 1040 | + } |
| 1041 | + return this; |
| 1042 | + }; |
| 1043 | + |
994 | 1044 | /**
|
995 | 1045 | * Reserve a chunk to be evaluated with the contents of a streamable.
|
996 | 1046 | * Currently an error event will bomb out the stream. Once an error
|
|
1002 | 1052 | * @return {Chunk}
|
1003 | 1053 | */
|
1004 | 1054 | Chunk.prototype.stream = function(stream, context, bodies, auto, filters) {
|
1005 |
| - var body = bodies && bodies.block, |
1006 |
| - errorBody = bodies && bodies.error; |
| 1055 | + var body = bodies && bodies.block; |
1007 | 1056 | return this.map(function(chunk) {
|
1008 | 1057 | var ended = false;
|
1009 | 1058 | stream
|
|
1025 | 1074 | if(ended) {
|
1026 | 1075 | return;
|
1027 | 1076 | }
|
1028 |
| - if(errorBody) { |
1029 |
| - chunk.render(errorBody, context.push(err)); |
1030 |
| - } else { |
1031 |
| - dust.log('Unhandled stream error in `' + context.getTemplateName() + '`', INFO); |
1032 |
| - } |
| 1077 | + chunk.renderError(err, context, bodies); |
| 1078 | + dust.log('Unhandled stream error in `' + context.getTemplateName() + '`', INFO); |
1033 | 1079 | if(!ended) {
|
1034 | 1080 | ended = true;
|
1035 | 1081 | chunk.end();
|
|
0 commit comments