Files
react/packages/react-dom/src/shared/DOMPropertyOperations.js
T
Dan Abramov d9c1dbd617 Use Yarn Workspaces (#11252)
* Enable Yarn workspaces for packages/*

* Move src/isomorphic/* into packages/react/src/*

* Create index.js stubs for all packages in packages/*

This makes the test pass again, but breaks the build because npm/ folders aren't used yet.
I'm not sure if we'll keep this structure--I'll just keep working and fix the build after it settles down.

* Put FB entry point for react-dom into packages/*

* Move src/renderers/testing/* into packages/react-test-renderer/src/*

Note that this is currently broken because Jest ignores node_modules,
and so Yarn linking makes Jest skip React source when transforming.

* Remove src/node_modules

It is now unnecessary. Some tests fail though.

* Add a hacky workaround for Jest/Workspaces issue

Jest sees node_modules and thinks it's third party code.

This is a hacky way to teach Jest to still transform anything in node_modules/react*
if it resolves outside of node_modules (such as to our packages/*) folder.

I'm not very happy with this and we should revisit.

* Add a fake react-native package

* Move src/renderers/art/* into packages/react-art/src/*

* Move src/renderers/noop/* into packages/react-noop-renderer/src/*

* Move src/renderers/dom/* into packages/react-dom/src/*

* Move src/renderers/shared/fiber/* into packages/react-reconciler/src/*

* Move DOM/reconciler tests I previously forgot to move

* Move src/renderers/native-*/* into packages/react-native-*/src/*

* Move shared code into packages/shared

It's not super clear how to organize this properly yet.

* Add back files that somehow got lost

* Fix the build

* Prettier

* Add missing license headers

* Fix an issue that caused mocks to get included into build

* Update other references to src/

* Re-run Prettier

* Fix lint

* Fix weird Flow violation

I didn't change this file but Flow started complaining.
Caleb said this annotation was unnecessarily using $Abstract though so I removed it.

* Update sizes

* Fix stats script

* Fix packaging fixtures

Use file: instead of NODE_PATH since NODE_PATH.
NODE_PATH trick only worked because we had no react/react-dom in root node_modules, but now we do.

file: dependency only works as I expect in Yarn, so I moved the packaging fixtures to use Yarn and committed lockfiles.
Verified that the page shows up.

* Fix art fixture

* Fix reconciler fixture

* Fix SSR fixture

* Rename native packages
2017-10-19 00:22:21 +01:00

260 lines
7.7 KiB
JavaScript

/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @providesModule DOMPropertyOperations
*/
'use strict';
var DOMProperty = require('DOMProperty');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
}
// isAttributeNameSafe() is currently duplicated in DOMMarkupOperations.
// TODO: Find a better place for this.
var VALID_ATTRIBUTE_NAME_REGEX = new RegExp(
'^[' +
DOMProperty.ATTRIBUTE_NAME_START_CHAR +
'][' +
DOMProperty.ATTRIBUTE_NAME_CHAR +
']*$',
);
var illegalAttributeNameCache = {};
var validatedAttributeNameCache = {};
function isAttributeNameSafe(attributeName) {
if (validatedAttributeNameCache.hasOwnProperty(attributeName)) {
return true;
}
if (illegalAttributeNameCache.hasOwnProperty(attributeName)) {
return false;
}
if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
validatedAttributeNameCache[attributeName] = true;
return true;
}
illegalAttributeNameCache[attributeName] = true;
if (__DEV__) {
warning(false, 'Invalid attribute name: `%s`', attributeName);
}
return false;
}
// shouldIgnoreValue() is currently duplicated in DOMMarkupOperations.
// TODO: Find a better place for this.
function shouldIgnoreValue(propertyInfo, value) {
return (
value == null ||
(propertyInfo.hasBooleanValue && !value) ||
(propertyInfo.hasNumericValue && isNaN(value)) ||
(propertyInfo.hasPositiveNumericValue && value < 1) ||
(propertyInfo.hasOverloadedBooleanValue && value === false)
);
}
/**
* Operations for dealing with DOM properties.
*/
var DOMPropertyOperations = {
setAttributeForID: function(node, id) {
node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id);
},
setAttributeForRoot: function(node) {
node.setAttribute(DOMProperty.ROOT_ATTRIBUTE_NAME, '');
},
/**
* Get the value for a property on a node. Only used in DEV for SSR validation.
* The "expected" argument is used as a hint of what the expected value is.
* Some properties have multiple equivalent values.
*/
getValueForProperty: function(node, name, expected) {
if (__DEV__) {
var propertyInfo = DOMProperty.getPropertyInfo(name);
if (propertyInfo) {
var mutationMethod = propertyInfo.mutationMethod;
if (mutationMethod || propertyInfo.mustUseProperty) {
return node[propertyInfo.propertyName];
} else {
var attributeName = propertyInfo.attributeName;
var stringValue = null;
if (propertyInfo.hasOverloadedBooleanValue) {
if (node.hasAttribute(attributeName)) {
var value = node.getAttribute(attributeName);
if (value === '') {
return true;
}
if (shouldIgnoreValue(propertyInfo, expected)) {
return value;
}
if (value === '' + expected) {
return expected;
}
return value;
}
} else if (node.hasAttribute(attributeName)) {
if (shouldIgnoreValue(propertyInfo, expected)) {
// We had an attribute but shouldn't have had one, so read it
// for the error message.
return node.getAttribute(attributeName);
}
if (propertyInfo.hasBooleanValue) {
// If this was a boolean, it doesn't matter what the value is
// the fact that we have it is the same as the expected.
return expected;
}
// Even if this property uses a namespace we use getAttribute
// because we assume its namespaced name is the same as our config.
// To use getAttributeNS we need the local name which we don't have
// in our config atm.
stringValue = node.getAttribute(attributeName);
}
if (shouldIgnoreValue(propertyInfo, expected)) {
return stringValue === null ? expected : stringValue;
} else if (stringValue === '' + expected) {
return expected;
} else {
return stringValue;
}
}
}
}
},
/**
* Get the value for a attribute on a node. Only used in DEV for SSR validation.
* The third argument is used as a hint of what the expected value is. Some
* attributes have multiple equivalent values.
*/
getValueForAttribute: function(node, name, expected) {
if (__DEV__) {
if (!isAttributeNameSafe(name)) {
return;
}
if (!node.hasAttribute(name)) {
return expected === undefined ? undefined : null;
}
var value = node.getAttribute(name);
if (value === '' + expected) {
return expected;
}
return value;
}
},
/**
* Sets the value for a property on a node.
*
* @param {DOMElement} node
* @param {string} name
* @param {*} value
*/
setValueForProperty: function(node, name, value) {
var propertyInfo = DOMProperty.getPropertyInfo(name);
if (propertyInfo && DOMProperty.shouldSetAttribute(name, value)) {
var mutationMethod = propertyInfo.mutationMethod;
if (mutationMethod) {
mutationMethod(node, value);
} else if (shouldIgnoreValue(propertyInfo, value)) {
DOMPropertyOperations.deleteValueForProperty(node, name);
return;
} else if (propertyInfo.mustUseProperty) {
// Contrary to `setAttribute`, object properties are properly
// `toString`ed by IE8/9.
node[propertyInfo.propertyName] = value;
} else {
var attributeName = propertyInfo.attributeName;
var namespace = propertyInfo.attributeNamespace;
// `setAttribute` with objects becomes only `[object]` in IE8/9,
// ('' + value) makes it output the correct toString()-value.
if (namespace) {
node.setAttributeNS(namespace, attributeName, '' + value);
} else if (
propertyInfo.hasBooleanValue ||
(propertyInfo.hasOverloadedBooleanValue && value === true)
) {
node.setAttribute(attributeName, '');
} else {
node.setAttribute(attributeName, '' + value);
}
}
} else {
DOMPropertyOperations.setValueForAttribute(
node,
name,
DOMProperty.shouldSetAttribute(name, value) ? value : null,
);
return;
}
if (__DEV__) {
var payload = {};
payload[name] = value;
}
},
setValueForAttribute: function(node, name, value) {
if (!isAttributeNameSafe(name)) {
return;
}
if (value == null) {
node.removeAttribute(name);
} else {
node.setAttribute(name, '' + value);
}
if (__DEV__) {
var payload = {};
payload[name] = value;
}
},
/**
* Deletes an attributes from a node.
*
* @param {DOMElement} node
* @param {string} name
*/
deleteValueForAttribute: function(node, name) {
node.removeAttribute(name);
},
/**
* Deletes the value for a property on a node.
*
* @param {DOMElement} node
* @param {string} name
*/
deleteValueForProperty: function(node, name) {
var propertyInfo = DOMProperty.getPropertyInfo(name);
if (propertyInfo) {
var mutationMethod = propertyInfo.mutationMethod;
if (mutationMethod) {
mutationMethod(node, undefined);
} else if (propertyInfo.mustUseProperty) {
var propName = propertyInfo.propertyName;
if (propertyInfo.hasBooleanValue) {
node[propName] = false;
} else {
node[propName] = '';
}
} else {
node.removeAttribute(propertyInfo.attributeName);
}
} else {
node.removeAttribute(name);
}
},
};
module.exports = DOMPropertyOperations;