Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81159 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule Danger
10
* @typechecks static-only
11
*/
12
13
/*jslint evil: true, sub: true */
14
15
"use strict";
16
17
var ExecutionEnvironment = require('ExecutionEnvironment');
18
19
var createNodesFromMarkup = require('createNodesFromMarkup');
20
var emptyFunction = require('emptyFunction');
21
var getMarkupWrap = require('getMarkupWrap');
22
var invariant = require('invariant');
23
24
var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
25
var RESULT_INDEX_ATTR = 'data-danger-index';
26
27
/**
28
* Extracts the `nodeName` from a string of markup.
29
*
30
* NOTE: Extracting the `nodeName` does not require a regular expression match
31
* because we make assumptions about React-generated markup (i.e. there are no
32
* spaces surrounding the opening tag and there is at least one attribute).
33
*
34
* @param {string} markup String of markup.
35
* @return {string} Node name of the supplied markup.
36
* @see http://jsperf.com/extract-nodename
37
*/
38
function getNodeName(markup) {
39
return markup.substring(1, markup.indexOf(' '));
40
}
41
42
var Danger = {
43
44
/**
45
* Renders markup into an array of nodes. The markup is expected to render
46
* into a list of root nodes. Also, the length of `resultList` and
47
* `markupList` should be the same.
48
*
49
* @param {array<string>} markupList List of markup strings to render.
50
* @return {array<DOMElement>} List of rendered nodes.
51
* @internal
52
*/
53
dangerouslyRenderMarkup: function(markupList) {
54
invariant(
55
ExecutionEnvironment.canUseDOM,
56
'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' +
57
'thread. Make sure `window` and `document` are available globally ' +
58
'before requiring React when unit testing or use ' +
59
'React.renderToString for server rendering.'
60
);
61
var nodeName;
62
var markupByNodeName = {};
63
// Group markup by `nodeName` if a wrap is necessary, else by '*'.
64
for (var i = 0; i < markupList.length; i++) {
65
invariant(
66
markupList[i],
67
'dangerouslyRenderMarkup(...): Missing markup.'
68
);
69
nodeName = getNodeName(markupList[i]);
70
nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
71
markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
72
markupByNodeName[nodeName][i] = markupList[i];
73
}
74
var resultList = [];
75
var resultListAssignmentCount = 0;
76
for (nodeName in markupByNodeName) {
77
if (!markupByNodeName.hasOwnProperty(nodeName)) {
78
continue;
79
}
80
var markupListByNodeName = markupByNodeName[nodeName];
81
82
// This for-in loop skips the holes of the sparse array. The order of
83
// iteration should follow the order of assignment, which happens to match
84
// numerical index order, but we don't rely on that.
85
for (var resultIndex in markupListByNodeName) {
86
if (markupListByNodeName.hasOwnProperty(resultIndex)) {
87
var markup = markupListByNodeName[resultIndex];
88
89
// Push the requested markup with an additional RESULT_INDEX_ATTR
90
// attribute. If the markup does not start with a < character, it
91
// will be discarded below (with an appropriate console.error).
92
markupListByNodeName[resultIndex] = markup.replace(
93
OPEN_TAG_NAME_EXP,
94
// This index will be parsed back out below.
95
'$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '
96
);
97
}
98
}
99
100
// Render each group of markup with similar wrapping `nodeName`.
101
var renderNodes = createNodesFromMarkup(
102
markupListByNodeName.join(''),
103
emptyFunction // Do nothing special with <script> tags.
104
);
105
106
for (i = 0; i < renderNodes.length; ++i) {
107
var renderNode = renderNodes[i];
108
if (renderNode.hasAttribute &&
109
renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
110
111
resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
112
renderNode.removeAttribute(RESULT_INDEX_ATTR);
113
114
invariant(
115
!resultList.hasOwnProperty(resultIndex),
116
'Danger: Assigning to an already-occupied result index.'
117
);
118
119
resultList[resultIndex] = renderNode;
120
121
// This should match resultList.length and markupList.length when
122
// we're done.
123
resultListAssignmentCount += 1;
124
125
} else if (__DEV__) {
126
console.error(
127
"Danger: Discarding unexpected node:",
128
renderNode
129
);
130
}
131
}
132
}
133
134
// Although resultList was populated out of order, it should now be a dense
135
// array.
136
invariant(
137
resultListAssignmentCount === resultList.length,
138
'Danger: Did not assign to every index of resultList.'
139
);
140
141
invariant(
142
resultList.length === markupList.length,
143
'Danger: Expected markup to render %s nodes, but rendered %s.',
144
markupList.length,
145
resultList.length
146
);
147
148
return resultList;
149
},
150
151
/**
152
* Replaces a node with a string of markup at its current position within its
153
* parent. The markup must render into a single root node.
154
*
155
* @param {DOMElement} oldChild Child node to replace.
156
* @param {string} markup Markup to render in place of the child node.
157
* @internal
158
*/
159
dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
160
invariant(
161
ExecutionEnvironment.canUseDOM,
162
'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
163
'worker thread. Make sure `window` and `document` are available ' +
164
'globally before requiring React when unit testing or use ' +
165
'React.renderToString for server rendering.'
166
);
167
invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.');
168
invariant(
169
oldChild.tagName.toLowerCase() !== 'html',
170
'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
171
'<html> node. This is because browser quirks make this unreliable ' +
172
'and/or slow. If you want to render to the root you must use ' +
173
'server rendering. See renderComponentToString().'
174
);
175
176
var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
177
oldChild.parentNode.replaceChild(newChild, oldChild);
178
}
179
180
};
181
182
module.exports = Danger;
183
184