react 0.14.0-alpha1 → 0.14.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -81,13 +81,12 @@ function wrapUserProvidedKey(key) {
81
81
  /**
82
82
  * @param {?*} children Children tree container.
83
83
  * @param {!string} nameSoFar Name of the key path so far.
84
- * @param {!number} indexSoFar Number of children encountered until this point.
85
84
  * @param {!function} callback Callback to invoke with each child found.
86
85
  * @param {?*} traverseContext Used to pass information throughout the traversal
87
86
  * process.
88
87
  * @return {!number} The number of children in this subtree.
89
88
  */
90
- function traverseAllChildrenImpl(children, nameSoFar, indexSoFar, callback, traverseContext) {
89
+ function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
91
90
  var type = typeof children;
92
91
 
93
92
  if (type === 'undefined' || type === 'boolean') {
@@ -99,19 +98,19 @@ function traverseAllChildrenImpl(children, nameSoFar, indexSoFar, callback, trav
99
98
  callback(traverseContext, children,
100
99
  // If it's the only child, treat the name as if it was wrapped in an array
101
100
  // so that it's consistent if the number of children grows.
102
- nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar, indexSoFar);
101
+ nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
103
102
  return 1;
104
103
  }
105
104
 
106
- var child, nextName, nextIndex;
105
+ var child;
106
+ var nextName;
107
107
  var subtreeCount = 0; // Count of children found in the current subtree.
108
108
 
109
109
  if (Array.isArray(children)) {
110
110
  for (var i = 0; i < children.length; i++) {
111
111
  child = children[i];
112
112
  nextName = (nameSoFar !== '' ? nameSoFar + SUBSEPARATOR : SEPARATOR) + getComponentKey(child, i);
113
- nextIndex = indexSoFar + subtreeCount;
114
- subtreeCount += traverseAllChildrenImpl(child, nextName, nextIndex, callback, traverseContext);
113
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
115
114
  }
116
115
  } else {
117
116
  var iteratorFn = getIteratorFn(children);
@@ -123,8 +122,7 @@ function traverseAllChildrenImpl(children, nameSoFar, indexSoFar, callback, trav
123
122
  while (!(step = iterator.next()).done) {
124
123
  child = step.value;
125
124
  nextName = (nameSoFar !== '' ? nameSoFar + SUBSEPARATOR : SEPARATOR) + getComponentKey(child, ii++);
126
- nextIndex = indexSoFar + subtreeCount;
127
- subtreeCount += traverseAllChildrenImpl(child, nextName, nextIndex, callback, traverseContext);
125
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
128
126
  }
129
127
  } else {
130
128
  if ('production' !== process.env.NODE_ENV) {
@@ -137,8 +135,7 @@ function traverseAllChildrenImpl(children, nameSoFar, indexSoFar, callback, trav
137
135
  if (entry) {
138
136
  child = entry[1];
139
137
  nextName = (nameSoFar !== '' ? nameSoFar + SUBSEPARATOR : SEPARATOR) + wrapUserProvidedKey(entry[0]) + SUBSEPARATOR + getComponentKey(child, 0);
140
- nextIndex = indexSoFar + subtreeCount;
141
- subtreeCount += traverseAllChildrenImpl(child, nextName, nextIndex, callback, traverseContext);
138
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
142
139
  }
143
140
  }
144
141
  }
@@ -149,8 +146,7 @@ function traverseAllChildrenImpl(children, nameSoFar, indexSoFar, callback, trav
149
146
  if (fragment.hasOwnProperty(key)) {
150
147
  child = fragment[key];
151
148
  nextName = (nameSoFar !== '' ? nameSoFar + SUBSEPARATOR : SEPARATOR) + wrapUserProvidedKey(key) + SUBSEPARATOR + getComponentKey(child, 0);
152
- nextIndex = indexSoFar + subtreeCount;
153
- subtreeCount += traverseAllChildrenImpl(child, nextName, nextIndex, callback, traverseContext);
149
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
154
150
  }
155
151
  }
156
152
  }
@@ -180,7 +176,7 @@ function traverseAllChildren(children, callback, traverseContext) {
180
176
  return 0;
181
177
  }
182
178
 
183
- return traverseAllChildrenImpl(children, '', 0, callback, traverseContext);
179
+ return traverseAllChildrenImpl(children, '', callback, traverseContext);
184
180
  }
185
181
 
186
182
  module.exports = traverseAllChildren;
@@ -11,6 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ var assign = require("./Object.assign");
14
15
  var emptyFunction = require("./emptyFunction");
15
16
  var warning = require("./warning");
16
17
 
@@ -31,81 +32,86 @@ if ('production' !== process.env.NODE_ENV) {
31
32
  // https://html.spec.whatwg.org/multipage/syntax.html#special
32
33
  var specialTags = ['address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp'];
33
34
 
34
- /**
35
- * Return whether `stack` contains `tag` and the last occurrence of `tag` is
36
- * deeper than any element in the `scope` array.
37
- *
38
- * https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-the-specific-scope
39
- *
40
- * Examples:
41
- * stackHasTagInSpecificScope(['p', 'quote'], 'p', ['button']) is true
42
- * stackHasTagInSpecificScope(['p', 'button'], 'p', ['button']) is false
43
- *
44
- * @param {Array<string>} stack
45
- * @param {string} tag
46
- * @param {Array<string>} scope
47
- */
48
- var stackHasTagInSpecificScope = function (stack, tag, scope) {
49
- for (var i = stack.length - 1; i >= 0; i--) {
50
- if (stack[i] === tag) {
51
- return true;
52
- }
53
- if (scope.indexOf(stack[i]) !== -1) {
54
- return false;
55
- }
56
- }
57
- return false;
58
- };
59
-
60
35
  // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
61
36
  var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template',
62
37
 
63
38
  // https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
64
- // TODO: Distinguish by namespace here
39
+ // TODO: Distinguish by namespace here -- for <title>, including it here
40
+ // errs on the side of fewer warnings
65
41
  'foreignObject', 'desc', 'title'];
66
- var stackHasTagInScope = function (stack, tag) {
67
- return stackHasTagInSpecificScope(stack, tag, inScopeTags);
68
- };
69
42
 
70
43
  // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
71
44
  var buttonScopeTags = inScopeTags.concat(['button']);
72
- var stackHasTagInButtonScope = function (stack, tag) {
73
- return stackHasTagInSpecificScope(stack, tag, buttonScopeTags);
45
+
46
+ // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
47
+ var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
48
+
49
+ var emptyAncestorInfo = {
50
+ parentTag: null,
51
+
52
+ formTag: null,
53
+ aTagInScope: null,
54
+ buttonTagInScope: null,
55
+ nobrTagInScope: null,
56
+ pTagInButtonScope: null,
57
+
58
+ listItemTagAutoclosing: null,
59
+ dlItemTagAutoclosing: null
74
60
  };
75
61
 
76
- // See rules for 'li', 'dd', 'dt' start tags in
77
- // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
78
- var listItemTagAllowed = function (tags, stack) {
79
- // tags is ['li'] or ['dd, 'dt']
80
- for (var i = stack.length - 1; i >= 0; i--) {
81
- if (tags.indexOf(stack[i]) !== -1) {
82
- return false;
83
- } else if (specialTags.indexOf(stack[i]) !== -1 && stack[i] !== 'address' && stack[i] !== 'div' && stack[i] !== 'p') {
84
- return true;
85
- }
62
+ var updatedAncestorInfo = function (oldInfo, tag, instance) {
63
+ var ancestorInfo = assign({}, oldInfo || emptyAncestorInfo);
64
+ var info = { tag: tag, instance: instance };
65
+
66
+ if (inScopeTags.indexOf(tag) !== -1) {
67
+ ancestorInfo.aTagInScope = null;
68
+ ancestorInfo.buttonTagInScope = null;
69
+ ancestorInfo.nobrTagInScope = null;
70
+ }
71
+ if (buttonScopeTags.indexOf(tag) !== -1) {
72
+ ancestorInfo.pTagInButtonScope = null;
86
73
  }
87
- return true;
88
- };
89
74
 
90
- // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
91
- var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
75
+ // See rules for 'li', 'dd', 'dt' start tags in
76
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
77
+ if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') {
78
+ ancestorInfo.listItemTagAutoclosing = null;
79
+ ancestorInfo.dlItemTagAutoclosing = null;
80
+ }
81
+
82
+ ancestorInfo.parentTag = info;
83
+
84
+ if (tag === 'form') {
85
+ ancestorInfo.formTag = info;
86
+ }
87
+ if (tag === 'a') {
88
+ ancestorInfo.aTagInScope = info;
89
+ }
90
+ if (tag === 'button') {
91
+ ancestorInfo.buttonTagInScope = info;
92
+ }
93
+ if (tag === 'nobr') {
94
+ ancestorInfo.nobrTagInScope = info;
95
+ }
96
+ if (tag === 'p') {
97
+ ancestorInfo.pTagInButtonScope = info;
98
+ }
99
+ if (tag === 'li') {
100
+ ancestorInfo.listItemTagAutoclosing = info;
101
+ }
102
+ if (tag === 'dd' || tag === 'dt') {
103
+ ancestorInfo.dlItemTagAutoclosing = info;
104
+ }
105
+
106
+ return ancestorInfo;
107
+ };
92
108
 
93
109
  /**
94
- * Returns whether we allow putting `tag` in the document if the current stack
95
- * of open tags is `openTagStack`.
96
- *
97
- * Examples:
98
- * isTagValidInContext('tr', [..., 'table', 'tbody']) is true
99
- * isTagValidInContext('tr', [..., 'table']) is false
100
- *
101
- * @param {string} tag Lowercase HTML tag name or node name like '#text'
102
- * @param {Array<string>} openTagStack
110
+ * Returns whether
103
111
  */
104
- var isTagValidInContext = function (tag, openTagStack) {
105
- var currentTag = openTagStack[openTagStack.length - 1];
106
-
112
+ var isTagValidWithParent = function (tag, parentTag) {
107
113
  // First, let's check if we're in an unusual parsing mode...
108
- switch (currentTag) {
114
+ switch (parentTag) {
109
115
  // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
110
116
  case 'select':
111
117
  return tag === 'option' || tag === 'optgroup' || tag === '#text';
@@ -151,6 +157,44 @@ if ('production' !== process.env.NODE_ENV) {
151
157
  // Probably in the "in body" parsing mode, so we outlaw only tag combos
152
158
  // where the parsing rules cause implicit opens or closes to be added.
153
159
  // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
160
+ switch (tag) {
161
+ case 'h1':
162
+ case 'h2':
163
+ case 'h3':
164
+ case 'h4':
165
+ case 'h5':
166
+ case 'h6':
167
+ return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6';
168
+
169
+ case 'rp':
170
+ case 'rt':
171
+ return impliedEndTags.indexOf(parentTag) === -1;
172
+
173
+ case 'caption':
174
+ case 'col':
175
+ case 'colgroup':
176
+ case 'frame':
177
+ case 'head':
178
+ case 'tbody':
179
+ case 'td':
180
+ case 'tfoot':
181
+ case 'th':
182
+ case 'thead':
183
+ case 'tr':
184
+ // These tags are only valid with a few parents that have special child
185
+ // parsing rules -- if we're down here, then none of those matched and
186
+ // so we allow it only if we don't know what the parent is, as all other
187
+ // cases are invalid.
188
+ return parentTag == null;
189
+ }
190
+
191
+ return true;
192
+ };
193
+
194
+ /**
195
+ * Returns whether
196
+ */
197
+ var findInvalidAncestorForTag = function (tag, ancestorInfo) {
154
198
  switch (tag) {
155
199
  case 'address':
156
200
  case 'article':
@@ -185,7 +229,6 @@ if ('production' !== process.env.NODE_ENV) {
185
229
  case 'hr':
186
230
 
187
231
  case 'xmp':
188
- return !stackHasTagInButtonScope(openTagStack, 'p');
189
232
 
190
233
  case 'h1':
191
234
  case 'h2':
@@ -193,72 +236,120 @@ if ('production' !== process.env.NODE_ENV) {
193
236
  case 'h4':
194
237
  case 'h5':
195
238
  case 'h6':
196
- return !stackHasTagInButtonScope(openTagStack, 'p') && currentTag !== 'h1' && currentTag !== 'h2' && currentTag !== 'h3' && currentTag !== 'h4' && currentTag !== 'h5' && currentTag !== 'h6';
239
+ return ancestorInfo.pTagInButtonScope;
197
240
 
198
241
  case 'form':
199
- return openTagStack.indexOf('form') === -1 && !stackHasTagInButtonScope(openTagStack, 'p');
242
+ return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
200
243
 
201
244
  case 'li':
202
- return listItemTagAllowed(['li'], openTagStack);
245
+ return ancestorInfo.listItemTagAutoclosing;
203
246
 
204
247
  case 'dd':
205
248
  case 'dt':
206
- return listItemTagAllowed(['dd', 'dt'], openTagStack);
249
+ return ancestorInfo.dlItemTagAutoclosing;
207
250
 
208
251
  case 'button':
209
- return !stackHasTagInScope(openTagStack, 'button');
252
+ return ancestorInfo.buttonTagInScope;
210
253
 
211
254
  case 'a':
212
255
  // Spec says something about storing a list of markers, but it sounds
213
256
  // equivalent to this check.
214
- return !stackHasTagInScope(openTagStack, 'a');
257
+ return ancestorInfo.aTagInScope;
215
258
 
216
259
  case 'nobr':
217
- return !stackHasTagInScope(openTagStack, 'nobr');
260
+ return ancestorInfo.nobrTagInScope;
261
+ }
218
262
 
219
- case 'rp':
220
- case 'rt':
221
- return impliedEndTags.indexOf(currentTag) === -1;
263
+ return null;
264
+ };
222
265
 
223
- case 'caption':
224
- case 'col':
225
- case 'colgroup':
226
- case 'frame':
227
- case 'head':
228
- case 'tbody':
229
- case 'td':
230
- case 'tfoot':
231
- case 'th':
232
- case 'thead':
233
- case 'tr':
234
- return currentTag === undefined;
266
+ /**
267
+ * Given a ReactCompositeComponent instance, return a list of its recursive
268
+ * owners, starting at the root and ending with the instance itself.
269
+ */
270
+ var findOwnerStack = function (instance) {
271
+ if (!instance) {
272
+ return [];
235
273
  }
236
274
 
237
- return true;
275
+ var stack = [];
276
+ /*eslint-disable space-after-keywords */
277
+ do {
278
+ /*eslint-enable space-after-keywords */
279
+ stack.push(instance);
280
+ } while (instance = instance._currentElement._owner);
281
+ stack.reverse();
282
+ return stack;
238
283
  };
239
284
 
240
- validateDOMNesting = function (parentStack, childTag, element) {
241
- if (!isTagValidInContext(childTag, parentStack)) {
242
- var info = '';
243
- var parentTag = parentStack[parentStack.length - 1];
244
- if (parentTag === 'table' && childTag === 'tr') {
245
- info += ' Add a <tbody> to your code to match the DOM tree generated by ' + 'the browser.';
246
- }
247
- if (element && element._owner) {
248
- var name = element._owner.getName();
249
- if (name) {
250
- info += ' Check the render method of `' + name + '`.';
285
+ validateDOMNesting = function (childTag, childInstance, ancestorInfo) {
286
+ ancestorInfo = ancestorInfo || emptyAncestorInfo;
287
+ var parentInfo = ancestorInfo.parentTag;
288
+ var parentTag = parentInfo && parentInfo.tag;
289
+
290
+ var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
291
+ var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo);
292
+ var problematic = invalidParent || invalidAncestor;
293
+
294
+ if (problematic) {
295
+ var ancestorTag = problematic.tag;
296
+ var ancestorInstance = problematic.instance;
297
+
298
+ var childOwner = childInstance && childInstance._currentElement._owner;
299
+ var ancestorOwner = ancestorInstance && ancestorInstance._currentElement._owner;
300
+
301
+ var childOwners = findOwnerStack(childOwner);
302
+ var ancestorOwners = findOwnerStack(ancestorOwner);
303
+
304
+ var minStackLen = Math.min(childOwners.length, ancestorOwners.length);
305
+ var i;
306
+
307
+ var deepestCommon = -1;
308
+ for (i = 0; i < minStackLen; i++) {
309
+ if (childOwners[i] === ancestorOwners[i]) {
310
+ deepestCommon = i;
311
+ } else {
312
+ break;
251
313
  }
252
314
  }
253
315
 
254
- 'production' !== process.env.NODE_ENV ? warning(false, 'validateDOMNesting(...): <%s> cannot appear as a child of <%s> ' + 'in this context (%s).%s', childTag, parentTag, parentStack.join(' > '), info) : null;
316
+ var UNKNOWN = '(unknown)';
317
+ var childOwnerNames = childOwners.slice(deepestCommon + 1).map(function (inst) {
318
+ return inst.getName() || UNKNOWN;
319
+ });
320
+ var ancestorOwnerNames = ancestorOwners.slice(deepestCommon + 1).map(function (inst) {
321
+ return inst.getName() || UNKNOWN;
322
+ });
323
+ var ownerInfo = [].concat(
324
+ // If the parent and child instances have a common owner ancestor, start
325
+ // with that -- otherwise we just start with the parent's owners.
326
+ deepestCommon !== -1 ? childOwners[deepestCommon].getName() || UNKNOWN : [], ancestorOwnerNames, ancestorTag,
327
+ // If we're warning about an invalid (non-parent) ancestry, add '...'
328
+ invalidAncestor ? ['...'] : [], childOwnerNames, childTag).join(' > ');
329
+
330
+ if (invalidParent) {
331
+ var info = '';
332
+ if (ancestorTag === 'table' && childTag === 'tr') {
333
+ info += ' Add a <tbody> to your code to match the DOM tree generated by ' + 'the browser.';
334
+ }
335
+ 'production' !== process.env.NODE_ENV ? warning(false, 'validateDOMNesting(...): <%s> cannot appear as a child of <%s>. ' + 'See %s.%s', childTag, ancestorTag, ownerInfo, info) : null;
336
+ } else {
337
+ 'production' !== process.env.NODE_ENV ? warning(false, 'validateDOMNesting(...): <%s> cannot appear as a descendant of ' + '<%s>. See %s.', childTag, ancestorTag, ownerInfo) : null;
338
+ }
255
339
  }
256
340
  };
257
341
 
258
- validateDOMNesting.tagStackContextKey = '__validateDOMNesting_tagStack$' + Math.random().toString(36).slice(2);
342
+ validateDOMNesting.ancestorInfoContextKey = '__validateDOMNesting_ancestorInfo$' + Math.random().toString(36).slice(2);
343
+
344
+ validateDOMNesting.updatedAncestorInfo = updatedAncestorInfo;
259
345
 
260
346
  // For testing
261
- validateDOMNesting.isTagValidInContext = isTagValidInContext;
347
+ validateDOMNesting.isTagValidInContext = function (tag, ancestorInfo) {
348
+ ancestorInfo = ancestorInfo || emptyAncestorInfo;
349
+ var parentInfo = ancestorInfo.parentTag;
350
+ var parentTag = parentInfo && parentInfo.tag;
351
+ return isTagValidWithParent(tag, parentTag) && !findInvalidAncestorForTag(tag, ancestorInfo);
352
+ };
262
353
  }
263
354
 
264
355
  module.exports = validateDOMNesting;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react",
3
3
  "description": "React is a JavaScript library for building user interfaces.",
4
- "version": "0.14.0-alpha1",
4
+ "version": "0.14.0-alpha2",
5
5
  "keywords": [
6
6
  "react"
7
7
  ],