Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #3596: Add warning for components not following proper case convention #7089

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/renderers/dom/client/__tests__/ReactMount-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ var ReactTestUtils;
var WebComponents;

describe('ReactMount', function() {

function normalizeCodeLocInfo(str) {
return str.replace(/\(at .+?:\d+\)/g, '(at **)');
}

beforeEach(function() {
jest.resetModuleRegistry();

Expand Down Expand Up @@ -163,6 +168,29 @@ describe('ReactMount', function() {
);
});

it('should warn when component does not follow proper case convention',
function() {
var container = document.createElement('container');
var myDiv = <div>React</div>;
var mySvg = <svg><polygon points="-145.6 556.9 104.8-145.6 429"/></svg>;
var centered = React.createClass({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the warning would fire even without this class being defined, right? This is effectively just catching unknown elements?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just delete this line.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the createClassline to test that the warning was actually triggering but yes feel free to delete it.

This is effectively just catching unknown elements

Correct as long as the element.type passes the criteria:

if (element == null || typeof element.type !== 'string' ||
    element.type.match(/\-|\s+/) !== null ||
    ReactDOMFactories.hasOwnProperty(element.type)) {
    return;
  }

render: function() {
return <div/>;
},
});
spyOn(console, 'error');
ReactDOM.render(myDiv, container);
ReactDOM.render(mySvg, container);
ReactDOM.render(<centered/>, container);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Unknown element centered. Did you miss-spell an HTML tag name? ' +
'If you are using a custom component, make sure your custom component ' +
'starts with an upper-case letter.' +
'\n in centered (at **)'
);
});

it('should account for escaping on a checksum mismatch', function() {
var div = document.createElement('div');
var markup = ReactDOMServer.renderToString(
Expand Down
2 changes: 2 additions & 0 deletions src/renderers/dom/shared/ReactDOMDebugTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

var ReactDOMNullInputValuePropDevtool = require('ReactDOMNullInputValuePropDevtool');
var ReactDOMUnknownPropertyDevtool = require('ReactDOMUnknownPropertyDevtool');
var ReactDOMUnknownComponentDevtool = require('ReactDOMUnknownComponentDevtool');
var ReactDebugTool = require('ReactDebugTool');

var warning = require('warning');
Expand Down Expand Up @@ -70,5 +71,6 @@ var ReactDOMDebugTool = {

ReactDOMDebugTool.addDevtool(ReactDOMUnknownPropertyDevtool);
ReactDOMDebugTool.addDevtool(ReactDOMNullInputValuePropDevtool);
ReactDOMDebugTool.addDevtool(ReactDOMUnknownComponentDevtool);

module.exports = ReactDOMDebugTool;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDOMUnknownComponentDevtool
*/

'use strict';

var ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
var warning = require('warning');
var ReactDOMFactories = require('ReactDOMFactories');
var didWarnAboutCase = false;

function handleElement(debugID, element) {
if (element == null || typeof element.type !== 'string' ||
element.type.match(/\-|\s+/) !== null ||
ReactDOMFactories.hasOwnProperty(element.type)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReactDOMFactories isn't really comprehensive and may go away in the near future, so I don't really trust this as a reliable source of information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation would require validating the tags against some kind of whitelist since HTMLUnknownElement isn't reliable either, so is there another reliable source? Seems like this PR would hinge on that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aweary Agreed, as far as I know there's none.
ReactDOMFactories was used since it's already being used by the project.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there isn't a secondary source to validate against to ensure that HTMLUnknownElement isn't throwing false positives we'd probably need to consider a different way to handle this. Keeping around ReactDOMFactories just for this case is likely not the best solution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aweary completely agree with you, but other than keeping a list of elements, how else can we make sure there's no false positives?

According to the the specs, SVG and MathML are elements allowed in HTML which belong to their own respective namespace, so createElement will return HTMLUnknownElement for any of these elements, aside from those, everything else should represent HTMLUnknownElement accurately.

Seems to me the solution for now is, we have to list svg/mathml valid tags and make sure we "ignore" those as valid.

In a perfect world we could get this data from W3C but I couldn't find anything useful to consume other than scraping their repo or MDN. They do have the useful tests that we can probably gather something from.

However this is essentially keeping a list of elements (kind of like the same ReactDOMFactories is doing) which it was already said is not the best solution. What do you guys think would be the ideal solution?

return;
}

var invalidElement = document.createElement(element.type) instanceof
HTMLUnknownElement;

if (!didWarnAboutCase && invalidElement) {
warning(false,
'Unknown element %s. Did you miss-spell an HTML tag name? ' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

miss-spell

Ironically, this is misspelled. You want "misspell".

'If you are using a custom component, make sure your custom component ' +
'starts with an upper-case letter.%s',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upper-case

We use "uppercase" elsewhere so let's do the same here.

element.type,
ReactComponentTreeDevtool.getStackAddendumByID(debugID)
);
didWarnAboutCase = true;
}
}

var ReactDOMUnknownComponentDevtool = {
onBeforeMountComponent(debugID, element) {
handleElement(debugID, element);
},
onBeforeUpdateComponent(debugID, element) {
handleElement(debugID, element);
},
};
module.exports = ReactDOMUnknownComponentDevtool;