hooks.macro
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -15,26 +15,60 @@ "use strict"; | ||
if (binding == null) { | ||
return false; | ||
return null; | ||
} | ||
return binding.scope === parentPath.scope; | ||
if (binding.scope !== parentPath.scope) { | ||
return null; | ||
} | ||
return binding; | ||
} | ||
function hookCreateTransform(parentPath, createPath, importedHookName, babel) { | ||
function visitInputsReferences(parentPath, entryPath, babel, visitor) { | ||
var t = babel.types; | ||
var references = []; | ||
createPath.traverse({ | ||
entryPath.traverse({ | ||
Expression: function Expression(path) { | ||
if (t.isIdentifier(path)) { | ||
if ( // Excluding "b" in "a.b" form | ||
(!t.isMemberExpression(path.parentPath) || path.parentKey === 'object') && // Excluding bindings outside of the component | ||
ensureParentScopeBinding(parentPath, path)) { | ||
if (!references.some(function (reference) { | ||
return reference.name === path.node.name; | ||
})) { | ||
references.push(path.node); | ||
if (!t.isIdentifier(path)) { | ||
return; | ||
} | ||
var binding = ensureParentScopeBinding(parentPath, path); // Excluding bindings outside of the component | ||
if (binding == null) { | ||
return; | ||
} | ||
if (t.isCallExpression(path.parentPath)) { | ||
if (t.isFunctionDeclaration(binding.path)) { | ||
visitInputsReferences(parentPath, binding.path, babel, visitor); | ||
} else if (t.isVariableDeclarator(binding.path)) { | ||
var initPath = binding.path.get('init'); | ||
if (t.isArrowFunctionExpression(initPath) || t.isFunctionExpression(initPath)) { | ||
visitInputsReferences(parentPath, initPath, babel, visitor); | ||
} else { | ||
visitor(path); | ||
} | ||
} else { | ||
visitor(path); | ||
} | ||
} | ||
} // Excluding "b" in "a.b" form | ||
else if (!t.isMemberExpression(path.parentPath) || path.parentKey === 'object') { | ||
visitor(path); | ||
} | ||
} | ||
}); | ||
} | ||
function hookCreateTransform(parentPath, createPath, importedHookName, babel) { | ||
var t = babel.types; | ||
var references = []; | ||
visitInputsReferences(parentPath, createPath, babel, function (_ref) { | ||
var node = _ref.node; | ||
if (!references.some(function (reference) { | ||
return reference.name === node.name; | ||
})) { | ||
references.push(node); | ||
} | ||
}); | ||
parentPath.replaceWith(t.callExpression(importedHookName, [createPath.node, t.arrayExpression(references)])); | ||
@@ -65,11 +99,11 @@ } | ||
function memoMacro(_ref) { | ||
var references = _ref.references, | ||
state = _ref.state, | ||
babel = _ref.babel; | ||
function memoMacro(_ref2) { | ||
var references = _ref2.references, | ||
state = _ref2.state, | ||
babel = _ref2.babel; | ||
var t = babel.types; | ||
CONFIGS.forEach(function (_ref2) { | ||
var macroName = _ref2[0], | ||
hookName = _ref2[1], | ||
autoClosure = _ref2[2]; | ||
CONFIGS.forEach(function (_ref3) { | ||
var macroName = _ref3[0], | ||
hookName = _ref3[1], | ||
autoClosure = _ref3[2]; | ||
@@ -76,0 +110,0 @@ if (references[macroName]) { |
{ | ||
"name": "hooks.macro", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"author": "Pier Paolo Ramon <ramonpierre@gmail.com>", | ||
@@ -5,0 +5,0 @@ "main": "build/hooks.macro.js", |
@@ -5,2 +5,14 @@ # Hooks’ Macro :umbrella: | ||
> **Note:** This is using the new [React Hooks API Proposal](https://reactjs.org/docs/hooks-intro.html) | ||
> which is subject to change until React 16.7 final. | ||
> | ||
> You'll need to install `react`, `react-dom`, etc at `^16.7.0-alpha.0` | ||
> | ||
> ``` | ||
> npm install react@next react-dom@next | ||
> ``` | ||
> ``` | ||
> yarn add react@next react-dom@next | ||
> ``` | ||
## Installation | ||
@@ -80,3 +92,3 @@ | ||
Exactly like React’s `useMemo` but automatically identifies value dependencies. | ||
Exactly like React’s `useCallback` but automatically identifies value dependencies. | ||
@@ -141,8 +153,21 @@ ```js | ||
You can work around this limitation by creating a variable which holds the current value, such as `const { current } = ref`. | ||
You can work around this limitation by creating a variable which holds the current value, such as `const { current } = ref`. | ||
3. Currently there’s no way to add additional keys for more fine grained cache invalidation. Could be an important escape hatch when you do nasty things, but in that case I’d prefer to use `useMemo`/`useCallback` directly. | ||
4. Only locally defined functions declarations and explicit function expressions (`let x = () => {}`) are traversed for indirect dependencies — all other function calls (such as `xxx()`) are treated as normal input dependencies and appended too. This is unnecessary (but not harmful) for setters coming from `useState`, and not an issue at all if the function is the result of `useCallback` or `useAutoCallback`. | ||
## Inspiration | ||
React [documentation about `useMemo`][0] and [`use*Effect`][1] hooks cites: (emphasis mine) | ||
> The array of inputs is not passed as arguments to the function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the inputs array. **In the future, a sufficiently advanced compiler could create this array automatically.** | ||
This project tries to cover exactly that: to create the inputs array automatically. | ||
[0]: https://reactjs.org/docs/hooks-reference.html#usememo | ||
[1]: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect | ||
## License | ||
MIT |
@@ -10,33 +10,68 @@ const { addNamed } = require('@babel/helper-module-imports'); | ||
if (binding == null) { | ||
return false; | ||
return null; | ||
} | ||
return binding.scope === parentPath.scope; | ||
if (binding.scope !== parentPath.scope) { | ||
return null; | ||
} | ||
return binding; | ||
} | ||
function hookCreateTransform(parentPath, createPath, importedHookName, babel) { | ||
function visitInputsReferences(parentPath, entryPath, babel, visitor) { | ||
const { types: t } = babel; | ||
const references = []; | ||
entryPath.traverse({ | ||
Expression(path) { | ||
if (!t.isIdentifier(path)) { | ||
return; | ||
} | ||
createPath.traverse({ | ||
Expression(path) { | ||
if (t.isIdentifier(path)) { | ||
if ( | ||
// Excluding "b" in "a.b" form | ||
(!t.isMemberExpression(path.parentPath) || | ||
path.parentKey === 'object') && | ||
// Excluding bindings outside of the component | ||
ensureParentScopeBinding(parentPath, path) | ||
) { | ||
const binding = ensureParentScopeBinding(parentPath, path); | ||
// Excluding bindings outside of the component | ||
if (binding == null) { | ||
return; | ||
} | ||
if (t.isCallExpression(path.parentPath)) { | ||
if (t.isFunctionDeclaration(binding.path)) { | ||
visitInputsReferences(parentPath, binding.path, babel, visitor); | ||
} else if (t.isVariableDeclarator(binding.path)) { | ||
const initPath = binding.path.get('init'); | ||
if ( | ||
!references.some(reference => reference.name === path.node.name) | ||
t.isArrowFunctionExpression(initPath) || | ||
t.isFunctionExpression(initPath) | ||
) { | ||
references.push(path.node); | ||
visitInputsReferences(parentPath, initPath, babel, visitor); | ||
} else { | ||
visitor(path); | ||
} | ||
} else { | ||
visitor(path); | ||
} | ||
} | ||
// Excluding "b" in "a.b" form | ||
else if ( | ||
!t.isMemberExpression(path.parentPath) || | ||
path.parentKey === 'object' | ||
) { | ||
visitor(path); | ||
} | ||
}, | ||
}); | ||
} | ||
function hookCreateTransform(parentPath, createPath, importedHookName, babel) { | ||
const { types: t } = babel; | ||
const references = []; | ||
visitInputsReferences(parentPath, createPath, babel, ({ node }) => { | ||
if (!references.some(reference => reference.name === node.name)) { | ||
references.push(node); | ||
} | ||
}); | ||
parentPath.replaceWith( | ||
@@ -43,0 +78,0 @@ t.callExpression(importedHookName, [ |
@@ -192,2 +192,136 @@ const path = require('path'); | ||
}, | ||
{ | ||
title: 'Works with funcs from props', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent({ onSomething }) { | ||
const value = 12; | ||
const result = useAutoMemo(() => onSomething(value)); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with funcs from props, again', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent({ onSomething }) { | ||
const value = 12; | ||
const result = useAutoMemo(() => { onSomething(value) }); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect dependencies (function declaration)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
function getDoubleValue() { | ||
return value * 2; | ||
} | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect dependencies (function expression)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
const getDoubleValue = function() { | ||
return value * 2; | ||
} | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect dependencies (arrow with expr)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
const getDoubleValue = () => value * 2; | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect dependencies (arrow with body)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
const getDoubleValue = () => { | ||
return value * 2; | ||
} | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect `let` dependencies (arrow with expr)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
let getDoubleValue = () => value * 2; | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with indirect `let` dependencies (arrow with body)', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
let getDoubleValue = () => { | ||
return value * 2; | ||
} | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with doubly indirect dependencies', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent() { | ||
const value = 12; | ||
function getValue() { | ||
return value; | ||
} | ||
function getDoubleValue() { | ||
return getValue() * 2; | ||
} | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
{ | ||
title: 'Works with doubly indirect dependencies on props', | ||
code: ` | ||
import { useAutoMemo } from './hooks.macro' | ||
function FakeComponent({ getValue }) { | ||
function getDoubleValue() { | ||
return getValue() * 2; | ||
} | ||
const result = useAutoMemo(() => getDoubleValue()); | ||
} | ||
`, | ||
}, | ||
]), | ||
@@ -194,0 +328,0 @@ }); |
Sorry, the diff of this file is not supported yet
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
37813
535
171