Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80529 views
1
/**
2
* Copyright 2013-2015, 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
("production" !== process.env.NODE_ENV ? 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
) : invariant(ExecutionEnvironment.canUseDOM));
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
("production" !== process.env.NODE_ENV ? invariant(
66
markupList[i],
67
'dangerouslyRenderMarkup(...): Missing markup.'
68
) : invariant(markupList[i]));
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
var resultIndex;
86
for (resultIndex in markupListByNodeName) {
87
if (markupListByNodeName.hasOwnProperty(resultIndex)) {
88
var markup = markupListByNodeName[resultIndex];
89
90
// Push the requested markup with an additional RESULT_INDEX_ATTR
91
// attribute. If the markup does not start with a < character, it
92
// will be discarded below (with an appropriate console.error).
93
markupListByNodeName[resultIndex] = markup.replace(
94
OPEN_TAG_NAME_EXP,
95
// This index will be parsed back out below.
96
'$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '
97
);
98
}
99
}
100
101
// Render each group of markup with similar wrapping `nodeName`.
102
var renderNodes = createNodesFromMarkup(
103
markupListByNodeName.join(''),
104
emptyFunction // Do nothing special with <script> tags.
105
);
106
107
for (var j = 0; j < renderNodes.length; ++j) {
108
var renderNode = renderNodes[j];
109
if (renderNode.hasAttribute &&
110
renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
111
112
resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
113
renderNode.removeAttribute(RESULT_INDEX_ATTR);
114
115
("production" !== process.env.NODE_ENV ? invariant(
116
!resultList.hasOwnProperty(resultIndex),
117
'Danger: Assigning to an already-occupied result index.'
118
) : invariant(!resultList.hasOwnProperty(resultIndex)));
119
120
resultList[resultIndex] = renderNode;
121
122
// This should match resultList.length and markupList.length when
123
// we're done.
124
resultListAssignmentCount += 1;
125
126
} else if ("production" !== process.env.NODE_ENV) {
127
console.error(
128
'Danger: Discarding unexpected node:',
129
renderNode
130
);
131
}
132
}
133
}
134
135
// Although resultList was populated out of order, it should now be a dense
136
// array.
137
("production" !== process.env.NODE_ENV ? invariant(
138
resultListAssignmentCount === resultList.length,
139
'Danger: Did not assign to every index of resultList.'
140
) : invariant(resultListAssignmentCount === resultList.length));
141
142
("production" !== process.env.NODE_ENV ? invariant(
143
resultList.length === markupList.length,
144
'Danger: Expected markup to render %s nodes, but rendered %s.',
145
markupList.length,
146
resultList.length
147
) : invariant(resultList.length === markupList.length));
148
149
return resultList;
150
},
151
152
/**
153
* Replaces a node with a string of markup at its current position within its
154
* parent. The markup must render into a single root node.
155
*
156
* @param {DOMElement} oldChild Child node to replace.
157
* @param {string} markup Markup to render in place of the child node.
158
* @internal
159
*/
160
dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
161
("production" !== process.env.NODE_ENV ? invariant(
162
ExecutionEnvironment.canUseDOM,
163
'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
164
'worker thread. Make sure `window` and `document` are available ' +
165
'globally before requiring React when unit testing or use ' +
166
'React.renderToString for server rendering.'
167
) : invariant(ExecutionEnvironment.canUseDOM));
168
("production" !== process.env.NODE_ENV ? invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : invariant(markup));
169
("production" !== process.env.NODE_ENV ? invariant(
170
oldChild.tagName.toLowerCase() !== 'html',
171
'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
172
'<html> node. This is because browser quirks make this unreliable ' +
173
'and/or slow. If you want to render to the root you must use ' +
174
'server rendering. See React.renderToString().'
175
) : invariant(oldChild.tagName.toLowerCase() !== 'html'));
176
177
var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
178
oldChild.parentNode.replaceChild(newChild, oldChild);
179
}
180
181
};
182
183
module.exports = Danger;
184
185