@samitouri / QOS-React-2 / commits / 5eb57fa51b

Add support to JSX transform for <hyphenated-tags>

This only adds support for whitelisted tags, not arbitrary ones. Closes #1539

Paul O’Shannessy committed Jun 30, 2014 at 20:47 UTC 5eb57fa51b4702e76e8c71535b695a216b803eb2
2 files changed +87 -9
vendor/fbtransform/transforms/__tests__/react-test.js
+53 -1
@@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 - * @emails javascript@lists.facebook.com
16 + * @emails jeffmo@fb.com
17 */
18 "use strict";
19
@@ -21,6 +21,12 @@ require('mock-modules').autoMockOff();
21
22 describe('react jsx', function() {
23 var transformAll = require('../../syntax.js').transformAll;
24 + var xjs = require('../xjs.js');
25 +
26 + // Add <font-face> to list of known tags to ensure that when we test support
27 + // for hyphentated known tags, it's there.
28 + // TODO: remove this when/if <font-face> is supported out of the box.
29 + xjs.knownTags['font-face'] = true;
30
31 var transform = function(code, options, excludes) {
32 return transformAll(
@@ -366,6 +372,23 @@ describe('react jsx', function() {
372 expect(transform(code).code).toBe(result);
373 });
374
375 + it('should allow deeper JS namespacing', function() {
376 + var code = [
377 + '/**',
378 + ' * @jsx React.DOM',
379 + ' */',
380 + '<Namespace.DeepNamespace.Component />;'
381 + ].join('\n');
382 + var result = [
383 + '/**',
384 + ' * @jsx React.DOM',
385 + ' */',
386 + 'Namespace.DeepNamespace.Component(null);'
387 + ].join('\n');
388 +
389 + expect(transform(code).code).toBe(result);
390 + });
391 +
392 it('should disallow XML namespacing', function() {
393 var code = [
394 '/**',
@@ -382,6 +405,24 @@ describe('react jsx', function() {
405 '<Component { ... x } y\n={2 } z />';
406 var result = HEADER +
407 'Component(Object.assign({}, x , {y: \n2, z: true}))';
408 +
409 + expect(transform(code).code).toBe(result);
410 + });
411 +
412 + it('should transform known hyphenated tags', function() {
413 + var code = [
414 + '/**',
415 + ' * @jsx React.DOM',
416 + ' */',
417 + '<font-face />;'
418 + ].join('\n');
419 + var result = [
420 + '/**',
421 + ' * @jsx React.DOM',
422 + ' */',
423 + 'React.DOM[\'font-face\'](null);'
424 + ].join('\n');
425 +
426 expect(transform(code).code).toBe(result);
427 });
428
@@ -391,6 +432,17 @@ describe('react jsx', function() {
432 ).not.toBeCalled();
433 });
434
435 + it('should throw for unknown hyphenated tags', function() {
436 + var code = [
437 + '/**',
438 + ' * @jsx React.DOM',
439 + ' */',
440 + '<x-component />;'
441 + ].join('\n');
442 +
443 + expect(() => transform(code)).toThrow();
444 + });
445 +
446 it('calls assign with a new target object for spreads', function() {
447 expectObjectAssign(
448 '<Component {...x} />'
vendor/fbtransform/transforms/react.js
+34 -8
@@ -65,20 +65,46 @@ function visitReactTag(traverse, object, path, state) {
65
66 utils.catchup(openingElement.range[0], state, trimLeft);
67
68 -
68 if (nameObject.type === Syntax.XJSNamespacedName && nameObject.namespace) {
69 throw new Error('Namespace tags are not supported. ReactJSX is not XML.');
70 }
71
73 - // Only identifiers can be fallback tags. XJSMemberExpressions are not.
74 - var isFallbackTag =
75 - nameObject.type === Syntax.XJSIdentifier &&
76 - FALLBACK_TAGS.hasOwnProperty(nameObject.name);
72 + // Only identifiers can be fallback tags or need quoting. We don't need to
73 + // handle quoting for other types.
74 + var didAddTag = false;
75
78 - utils.append(isFallbackTag ? jsxObjIdent + '.' : '', state);
76 + // Only identifiers can be fallback tags. XJSMemberExpressions are not.
77 + if (nameObject.type === Syntax.XJSIdentifier) {
78 + var tagName = nameObject.name;
79 + var quotedTagName = quoteAttrName(tagName);
80 +
81 + if (FALLBACK_TAGS.hasOwnProperty(tagName)) {
82 + // "Properly" handle invalid identifiers, like <font-face>, which needs to
83 + // be enclosed in quotes.
84 + var predicate =
85 + tagName === quotedTagName ?
86 + ('.' + tagName) :
87 + ('[' + quotedTagName + ']');
88 + utils.append(jsxObjIdent + predicate, state);
89 + utils.move(nameObject.range[1], state);
90 + didAddTag = true;
91 + } else if (tagName !== quotedTagName) {
92 + // If we're in the case where we need to quote and but don't recognize the
93 + // tag, throw.
94 + throw new Error(
95 + 'Tags must be valid JS identifiers or a recognized special case. `<' +
96 + tagName + '>` is not one of them.'
97 + );
98 + }
99 + }
100
80 - utils.move(nameObject.range[0], state);
81 - utils.catchup(nameObject.range[1], state);
101 + // Use utils.catchup in this case so we can easily handle XJSMemberExpressions
102 + // which look like Foo.Bar.Baz. This also handles unhyphenated XJSIdentifiers
103 + // that aren't fallback tags.
104 + if (!didAddTag) {
105 + utils.move(nameObject.range[0], state);
106 + utils.catchup(nameObject.range[1], state);
107 + }
108
109 utils.append('(', state);
110