@fullstory/babel-plugin-react-native
Advanced tools
Comparing version 1.0.3 to 1.1.0
179
lib/index.js
@@ -6,36 +6,13 @@ "use strict"; | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
exports.default = function (_ref) { | ||
var t = _ref.types; | ||
return { | ||
visitor: { | ||
/* Looks like we don't currently need to add this to the interface declaration | ||
InterfaceDeclaration(path, state) { | ||
fixUIManagerJSInterface(path); | ||
}, */ | ||
ClassDeclaration: function ClassDeclaration(path, state) { | ||
fixPressability(t, path); | ||
}, | ||
VariableDeclaration: function VariableDeclaration(path, state) { | ||
fixReactNativeViewConfig(path); | ||
fixReactNativeViewAttributes(path); | ||
fixTouchableMixin(t, path); | ||
} | ||
} | ||
}; | ||
exports.default = _default; | ||
var babylon = _interopRequireWildcard(require("@babel/parser")); | ||
var t = _interopRequireWildcard(require("@babel/types")); | ||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } | ||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } | ||
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } | ||
// We only add our ref to all Symbol(react.forward_ref) and Symbol(react.element) types, since they support refs | ||
var _createFabricRefCode = function _createFabricRefCode(refIdentifier, typeIdentifier, propsIdentifier) { | ||
return "\n const SUPPORTED_FS_ATTRIBUTES = [\n 'fsClass',\n 'fsAttribute',\n 'fsTagName',\n 'dataElement',\n 'dataComponent',\n 'dataSourceFile',\n ]; \n if (global.__turboModuleProxy != null && Platform.OS === 'ios') {\n if (".concat(typeIdentifier, ".$$typeof && (").concat(typeIdentifier, ".$$typeof.toString() === 'Symbol(react.forward_ref)' || ").concat(typeIdentifier, ".$$typeof.toString() === 'Symbol(react.element)')) {\n if (").concat(propsIdentifier, ") {\n const propContainsFSAttribute = SUPPORTED_FS_ATTRIBUTES.some(fsAttribute => {\n return typeof ").concat(propsIdentifier, "[fsAttribute] === 'string' && !!").concat(propsIdentifier, "[fsAttribute];\n });\n \n if (propContainsFSAttribute) {\n const fs = require('@fullstory/react-native');\n ").concat(refIdentifier, " = fs.applyFSPropertiesWithRef(").concat(refIdentifier, ");\n }\n }\n }\n }"); | ||
}; | ||
var _parser = require("@babel/parser"); | ||
var babylon = _interopRequireWildcard(_parser); | ||
var _types = require("@babel/types"); | ||
var t = _interopRequireWildcard(_types); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
// This is the code that we will generate for Pressability. | ||
@@ -46,3 +23,2 @@ // Note that `typeof UIManager` will cause an exception, so we use a try/catch. | ||
var _onFsPressForwardCallPress_PressabilityCode = "this._onFsPressForward_Pressability(false)"; | ||
var _onFsPressForward_PressabilityAst = babylon.parseExpression(_onFsPressForward_PressabilityCode, {}); | ||
@@ -57,3 +33,2 @@ var _onFsPressForwardCallLongPress_PressabilityAst = babylon.parseExpression(_onFsPressForwardCallLongPress_PressabilityCode, {}); | ||
var _onFsPressForwardCallPressCode = "this._onFsPressForward(false)"; | ||
var _onFsPressForwardAst = babylon.parseExpression(_onFsPressForwardCode, {}); | ||
@@ -66,6 +41,5 @@ var _onFsPressForwardCallLongPressAst = babylon.parseExpression(_onFsPressForwardCallLongPressCode, {}); | ||
var indent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2; | ||
var cache = []; | ||
var retVal = JSON.stringify(obj, function (key, value) { | ||
return (typeof value === "undefined" ? "undefined" : _typeof(value)) === 'object' && value !== null ? cache.includes(value) ? undefined // Duplicate reference found, discard key | ||
return _typeof(value) === 'object' && value !== null ? cache.includes(value) ? undefined // Duplicate reference found, discard key | ||
: cache.push(value) && value // Store value in our collection | ||
@@ -77,3 +51,2 @@ : value; | ||
} | ||
function addFullStoryPressHandlerDeclaration(props) { | ||
@@ -84,3 +57,2 @@ var prop = {}; | ||
prop.value = {}; | ||
prop.value.type = 'FunctionTypeAnnotation'; | ||
@@ -92,14 +64,10 @@ prop.value.params = []; | ||
prop.value.params.push(t.functionTypeParam(t.identifier('hasLongPressHandler'), t.booleanTypeAnnotation())); | ||
prop.value.rest = null; | ||
prop.value.returnType = t.voidTypeAnnotation(); | ||
prop.value.typeParameters = null; | ||
prop.optional = false; | ||
prop.static = false; | ||
prop.variance = null; | ||
props.push(prop); | ||
} | ||
function addFullStoryProperties(properties) { | ||
@@ -124,3 +92,2 @@ // add fsAttribute | ||
} | ||
function fixReactNativeViewConfig(path) { | ||
@@ -131,3 +98,2 @@ // make sure that this path has a node with a declarations array of size 1 | ||
} | ||
var declaration = path.node.declarations[0]; | ||
@@ -143,3 +109,2 @@ // validate that there is a name node and its value is what we expect | ||
} | ||
var props = declaration.init.properties; | ||
@@ -162,3 +127,2 @@ for (var i = 0; i < props.length; i++) { | ||
} | ||
function fixReactNativeViewAttributes(path) { | ||
@@ -169,3 +133,2 @@ // make sure that this path has a node with a declarations array of size 1 | ||
} | ||
var declaration = path.node.declarations[0]; | ||
@@ -181,3 +144,2 @@ // validate that there is a name node and its value is what we expect | ||
} | ||
var props = declaration.init.properties; | ||
@@ -195,3 +157,2 @@ for (var i = 0; i < props.length; i++) { | ||
} | ||
function fixUIManagerJSInterface(path) { | ||
@@ -207,3 +168,2 @@ // make sure that this path has a node with an id named what we expect | ||
} | ||
for (var i = 0; i < path.node.body.properties.length; i++) { | ||
@@ -220,3 +180,2 @@ var key = path.node.body.properties[i].key; | ||
} | ||
function fixPressability(t, path) { | ||
@@ -232,3 +191,2 @@ // make sure that this path has a node with an id named what we expect | ||
} | ||
var bodyArray = path.node.body.body; | ||
@@ -258,3 +216,3 @@ | ||
// now add the new ClassMethod to the bodyArray | ||
bodyArray.push(t.classMethod("method", t.identifier("_onFsPressForward_Pressability"), _onFsPressForward_PressabilityAst.right.params, _onFsPressForward_PressabilityAst.right.body, false, false)); | ||
bodyArray.push(t.classMethod('method', t.identifier('_onFsPressForward_Pressability'), _onFsPressForward_PressabilityAst.right.params, _onFsPressForward_PressabilityAst.right.body, false, false)); | ||
} else { | ||
@@ -267,3 +225,2 @@ // skip further processing on this object property | ||
} | ||
function fixTouchableMixin(t, path) { | ||
@@ -274,3 +231,2 @@ // make sure that this path has a node with a declarations array | ||
} | ||
var mixin = null; | ||
@@ -319,3 +275,3 @@ for (var i = 0; i < path.node.declarations.length; i++) { | ||
// now add the new ObjectProperty to the parent path | ||
mixin.init.properties.push(t.objectProperty(t.identifier("_onFsPressForward"), _onFsPressForwardAst.right)); | ||
mixin.init.properties.push(t.objectProperty(t.identifier('_onFsPressForward'), _onFsPressForwardAst.right)); | ||
} else { | ||
@@ -328,3 +284,112 @@ // skip further processing on this object property | ||
} | ||
function extendReactElementWithRef(path) { | ||
/* We're looking for the bit where react actually creates a ReactElement. | ||
* We identify this by it being an object that has a ref property, and | ||
* then after that, we verify that there is a $$typeof property that is | ||
* the magic ReactElement. i.e., we're looking for a whole object with: | ||
* | ||
* var blah1 = Symbol.for('react.element'); | ||
* [...] | ||
* var blah2; | ||
* { $$typeof: blah1, ..., ref: blah2, ... } | ||
*/ | ||
/* eslint-disable complexity */ | ||
// Are we actually looking at the ref: in there, and does it refer to a single variable? | ||
if (path.node.key.name !== 'ref' || !t.isIdentifier(path.node.value)) { | ||
return; | ||
} | ||
// Make sure that we have the $$typeof as a sibling, and it has a variable | ||
// reference as its contents. | ||
var typeofIdentifierNode = path.parentPath.node.properties.find(function (property) { | ||
return t.isObjectProperty(property) && property.key.name === '$$typeof'; | ||
}); | ||
if (!typeofIdentifierNode || !t.isIdentifier(typeofIdentifierNode.value)) { | ||
return; | ||
} | ||
// In this case, the typeofDeclPath points to the 'var blah1' declaration | ||
// in the above snippet; make sure it's of the form we expect it to be. | ||
var typeofDeclPath = path.scope.getBinding(typeofIdentifierNode.value.name).path; | ||
if (!t.isVariableDeclarator(typeofDeclPath.node) || !t.isCallExpression(typeofDeclPath.node.init) || | ||
// we could validate typeofDeclPath.node.init.callee to make sure it is 'Symbol.for', but life is too long | ||
typeofDeclPath.node.init.arguments.length != 1 || !t.isStringLiteral(typeofDeclPath.node.init.arguments[0]) || typeofDeclPath.node.init.arguments[0].value != 'react.element') { | ||
return; | ||
} | ||
// Need to dynamically grab variable names for variables since minified | ||
// code will change variable names; this is the lookup for the "var blah2" | ||
// above. | ||
var refIdentifier = path.node.value.name; | ||
var typeIdentifierNode = path.parentPath.node.properties.find(function (property) { | ||
return t.isObjectProperty(property) && property.key.name === 'type'; | ||
}); | ||
var propsIdentifierNode = path.parentPath.node.properties.find(function (property) { | ||
return t.isObjectProperty(property) && property.key.name === 'props'; | ||
}); | ||
var typeIdentifierValueIsIdentifier = t.isIdentifier(typeIdentifierNode.value); | ||
// variable "type" is a MemberExpression in production code | ||
var typeIdentifierValueIsMemberExpression = t.isMemberExpression(typeIdentifierNode.value); | ||
if (!(typeIdentifierValueIsIdentifier || typeIdentifierValueIsMemberExpression) || !t.isIdentifier(propsIdentifierNode.value)) { | ||
return; | ||
} | ||
var typeIdentifier = typeIdentifierValueIsIdentifier ? typeIdentifierNode.value.name : typeIdentifierNode.value.object.name; | ||
var propsIdentifier = propsIdentifierNode.value.name; | ||
// at long last, insert our code before the object declaration | ||
// https://github.com/facebook/react/blob/bbb9cb116dbf7b6247721aa0c4bcb6ec249aa8af/packages/react/src/ReactElement.js#L149 | ||
var fabricRefCodeAST = babylon.parse(_createFabricRefCode(refIdentifier, typeIdentifier, propsIdentifier)); | ||
path.getStatementParent().insertBefore(fabricRefCodeAST.program.body); | ||
} | ||
/* eslint-disable complexity */ | ||
function _default(_ref) { | ||
var t = _ref.types; | ||
return { | ||
visitor: { | ||
/* Looks like we don't currently need to add this to the interface declaration | ||
InterfaceDeclaration(path, state) { | ||
fixUIManagerJSInterface(path); | ||
}, */ | ||
ClassDeclaration: function ClassDeclaration(path, state) { | ||
fixPressability(t, path); | ||
}, | ||
VariableDeclaration: function VariableDeclaration(path, state) { | ||
fixReactNativeViewConfig(path); | ||
fixReactNativeViewAttributes(path); | ||
fixTouchableMixin(t, path); | ||
}, | ||
ObjectProperty: function ObjectProperty(path, state) { | ||
var reactFilesRegex = /node_modules\/react\/cjs\/.*\.js$/; | ||
// only rewrite files in react/cjs directory | ||
if (reactFilesRegex.test(state.file.opts.filename)) { | ||
extendReactElementWithRef(path); | ||
} | ||
}, | ||
JSXAttribute: function JSXAttribute(path) { | ||
// disable view optimization for only View component | ||
if (path.parent.name.name !== 'View') return; | ||
// must be manually annotated with at least one fs attribute | ||
if (path.node.name.name !== 'fsClass' && path.node.name.name !== 'fsTagName' && path.node.name.name !== 'fsAttribute') { | ||
return; | ||
} | ||
// check if view optimization is already disabled | ||
var isViewOptimizationDisabled = path.container.some(function (attribute) { | ||
return t.isJSXIdentifier(attribute.name, { | ||
name: 'testID' | ||
}) || t.isJSXIdentifier(attribute.name, { | ||
name: 'id' | ||
}) || t.isJSXIdentifier(attribute.name, { | ||
name: 'nativeID' | ||
}); | ||
}); | ||
if (isViewOptimizationDisabled) { | ||
return; | ||
} | ||
path.insertAfter(t.jsxAttribute(t.jsxIdentifier('nativeID'), t.stringLiteral('__FS_NATIVEID'))); | ||
} | ||
} | ||
}; | ||
} |
{ | ||
"name": "@fullstory/babel-plugin-react-native", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "The official FullStory React Native babel plugin", | ||
@@ -23,3 +23,5 @@ "repository": "git://github.com/fullstorydev/fullstory-babel-plugin-react-native.git", | ||
"build": "babel src -d lib", | ||
"test": "mocha --require babel-core/register" | ||
"lint": "eslint ./src", | ||
"test": "jest", | ||
"test:watch": "jest --watch" | ||
}, | ||
@@ -31,15 +33,28 @@ "dependencies": { | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-core": "^6.26.3", | ||
"babel-helper-plugin-test-runner": "^6.24.1", | ||
"babel-plugin-syntax-jsx": "^6.18.0", | ||
"babel-plugin-syntax-flow": "^6.18.0", | ||
"babel-preset-env": "^1.7.0", | ||
"mocha": "^9.2.2" | ||
"@babel/cli": "^7.0.0", | ||
"@babel/core": "^7.0.3", | ||
"@babel/eslint-parser": "^7.22.15", | ||
"@babel/helper-plugin-test-runner": "^7.0.0", | ||
"@babel/plugin-syntax-flow": "^7.0.0", | ||
"@babel/plugin-syntax-jsx": "^7.0.0", | ||
"@babel/preset-env": "7.0.0", | ||
"babel-plugin-tester": "^11.0.4", | ||
"eslint": "^8.52.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"jest": "^29.7.0", | ||
"prettier": "3.0.3" | ||
}, | ||
"jest": { | ||
"roots": [ | ||
"<rootDir>/__tests__/" | ||
], | ||
"testPathIgnorePatterns": [ | ||
"<rootDir>/__tests__/fixtures/*" | ||
] | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"env" | ||
"@babel/preset-env" | ||
] | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
23831
351
0
12
4