Skip to content

Commit

Permalink
Be smarter with when to wrap in IIFE (#37)
Browse files Browse the repository at this point in the history
* refactor `shouldWrapInClosure` to be more based on the owner than the contents

* handle `await` expressions

* handle JSX usage better

* clean up commented code

* update CHANGELOG
  • Loading branch information
planttheidea authored Dec 19, 2023
1 parent 0c90e0d commit 111cfa7
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 68 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## CHANGELOG

# 2.0.4

- [#37](https://github.com/planttheidea/inline-loops.macro/pull/37/files) - Smarter IIFE wrapper determination logic

# 2.0.3

- [#36](https://github.com/planttheidea/inline-loops.macro/pull/36) - Allow readonly collections, and fix `reduce*` method types for callback
Expand Down
5 changes: 5 additions & 0 deletions __tests__/__fixtures__/complex/array-element/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { map } from '../../../../src/inline-loops.macro';

function getStuff(array) {
return [array, map(array, (v) => v * 2)];
}
9 changes: 9 additions & 0 deletions __tests__/__fixtures__/complex/array-element/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function getStuff(array) {
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _v; _key < _length; ++_key) {
_v = array[_key];
_results[_key] = _v * 2;
}
return [array, _results];
}
20 changes: 9 additions & 11 deletions __tests__/__fixtures__/complex/for-each-hoisted/output.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { log } from './log';
function logItems(items) {
(() => {
const _fn = (_item) => {
if (!this.position) {
return;
}
log(_item);
};
for (let _key = 0, _length = items.length, _item; _key < _length; ++_key) {
_item = items[_key];
_fn(_item, _key, items);
const _fn = (_item) => {
if (!this.position) {
return;
}
})();
log(_item);
};
for (let _key = 0, _length = items.length, _item; _key < _length; ++_key) {
_item = items[_key];
_fn(_item, _key, items);
}
}
20 changes: 9 additions & 11 deletions __tests__/__fixtures__/complex/for-each-right-hoisted/output.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { log } from './log';
function logItems(items) {
(() => {
const _fn = (_item) => {
if (!this.position) {
return;
}
log(_item);
};
for (let _key = items.length, _item; --_key >= 0; ) {
_item = items[_key];
_fn(_item, _key, items);
const _fn = (_item) => {
if (!this.position) {
return;
}
})();
log(_item);
};
for (let _key = items.length, _item; --_key >= 0; ) {
_item = items[_key];
_fn(_item, _key, items);
}
}
14 changes: 14 additions & 0 deletions __tests__/__fixtures__/complex/if-statement-object/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { mapObject } from '../../../../src/inline-loops.macro';

function getStuff(object, foo) {
if (foo === 'bar') {
const state = {
...state,
foo,
};

return mapObject(object, (v) => v * 2);
}

return object;
}
18 changes: 18 additions & 0 deletions __tests__/__fixtures__/complex/if-statement-object/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function getStuff(object, foo) {
if (foo === 'bar') {
const state = {
...state,
foo,
};
return (() => {
const _results = {};
let _v;
for (const _key in object) {
_v = object[_key];
_results[_key] = _v * 2;
}
return _results;
})();
}
return object;
}
16 changes: 9 additions & 7 deletions __tests__/__fixtures__/complex/if-statement/output.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
function getStuff(array, foo) {
if (foo === 'bar') {
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _v; _key < _length; ++_key) {
_v = array[_key];
_results[_key] = _v * 2;
}
return _results;
return (() => {
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _v; _key < _length; ++_key) {
_v = array[_key];
_results[_key] = _v * 2;
}
return _results;
})();
}
return array;
}
12 changes: 12 additions & 0 deletions __tests__/__fixtures__/complex/jsx-conditional/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import { map } from '../../../../src/inline-loops.macro';

function List(props) {
return (
<ul>
{props.items &&
map(props.items, (item) => <li key={item.id}>{item.value}</li>)}
</ul>
);
}
23 changes: 23 additions & 0 deletions __tests__/__fixtures__/complex/jsx-conditional/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
function List(props) {
return /*#__PURE__*/ React.createElement(
'ul',
null,
props.items &&
(() => {
const _length = props.items.length;
const _results = Array(_length);
for (let _key = 0, _item; _key < _length; ++_key) {
_item = props.items[_key];
_results[_key] = /*#__PURE__*/ React.createElement(
'li',
{
key: _item.id,
},
_item.value,
);
}
return _results;
})(),
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { reduce } from '../../../../src/inline-loops.macro';

function getStuff(array, override) {
return override || reduce(array, (a, v) => a + v * 2, 0) || 1;
}
14 changes: 14 additions & 0 deletions __tests__/__fixtures__/complex/logical-expression-nested/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function getStuff(array, override) {
return (
override ||
(() => {
let _a = 0;
for (let _key = 0, _length = array.length, _v; _key < _length; ++_key) {
_v = array[_key];
_a = _a + _v * 2;
}
return _a;
})() ||
1
);
}
8 changes: 8 additions & 0 deletions __tests__/__fixtures__/complex/object-property/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { map } from '../../../../src/inline-loops.macro';

function getStuff(array) {
return {
array,
doubled: map(array, (v) => v * 2),
};
}
12 changes: 12 additions & 0 deletions __tests__/__fixtures__/complex/object-property/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function getStuff(array) {
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _v; _key < _length; ++_key) {
_v = array[_key];
_results[_key] = _v * 2;
}
return {
array,
doubled: _results,
};
}
5 changes: 5 additions & 0 deletions __tests__/__fixtures__/complex/ternary-nested/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { reduce } from '../../../../src/inline-loops.macro';

function getStuff(array, foo) {
return foo ? (reduce(array, (a, v) => a + v * 2, 0) ? 'stuff' : null) : null;
}
14 changes: 14 additions & 0 deletions __tests__/__fixtures__/complex/ternary-nested/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function getStuff(array, foo) {
return foo
? (() => {
let _a = 0;
for (let _key = 0, _length = array.length, _v; _key < _length; ++_key) {
_v = array[_key];
_a = _a + _v * 2;
}
return _a;
})()
? 'stuff'
: null
: null;
}
22 changes: 10 additions & 12 deletions __tests__/__fixtures__/complex/this/output.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
function foo(array) {
return (() => {
const _fn = function (_value) {
return this && this.foo ? _value : null;
};
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _value; _key < _length; ++_key) {
_value = array[_key];
_results[_key] = _fn(_value, _key, array);
}
return _results;
})();
const _fn = function (_value) {
return this && this.foo ? _value : null;
};
const _length = array.length;
const _results = Array(_length);
for (let _key = 0, _value; _key < _length; ++_key) {
_value = array[_key];
_results[_key] = _fn(_value, _key, array);
}
return _results;
}
70 changes: 43 additions & 27 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export function replaceOrRemove(
templates: ReturnType<typeof createTemplates>,
replacement: Expression,
) {
if (shouldWrapInClosure(path, local)) {
if (shouldWrapInClosure(path)) {
if (!t.isIdentifier(replacement, { name: 'undefined' })) {
local.contents.push(t.returnStatement(replacement));
}
Expand Down Expand Up @@ -214,49 +214,65 @@ export function isPossiblyDynamic(path: Path) {
return path.isExpression();
}

export function shouldWrapInClosure(
path: Path<CallExpression>,
local: LocalReferences,
): boolean {
export function shouldWrapInClosure(path: Path): boolean {
const parentPath = path.parentPath;

if (!parentPath) {
return false;
}

const functionParent = path.getFunctionParent();

if (!functionParent) {
return isPossiblyDynamic(parentPath);
}

const grandparentPath = parentPath.parentPath;

if (parentPath.isPattern() || local.contents.length > 1) {
if (parentPath.isAssignmentPattern()) {
return true;
}

const contents = functionParent?.get('body')?.get('body');

if (Array.isArray(contents) && contents.length > 1) {
return contents.flat().some((content) => content.isVariableDeclaration());
}

if (!grandparentPath || !parentPath.isExpression()) {
return false;
if (
parentPath.isArrayExpression() ||
parentPath.isAwaitExpression() ||
parentPath.isBinaryExpression() ||
parentPath.isJSXElement() ||
parentPath.isJSXExpressionContainer() ||
parentPath.isObjectExpression() ||
parentPath.isObjectProperty() ||
parentPath.isUnaryExpression()
) {
return shouldWrapInClosure(parentPath);
}

const maybeNestedConditional = isPossiblyDynamic(grandparentPath);

if (parentPath.isLogicalExpression()) {
return (
!maybeNestedConditional && parentPath.get('right').node === path.node
!path.isCallExpression() ||
parentPath.get('right').node === path.node ||
shouldWrapInClosure(parentPath)
);
}

if (parentPath.isConditionalExpression()) {
return !maybeNestedConditional && parentPath.get('test').node !== path.node;
return (
!path.isCallExpression() ||
parentPath.get('test').node !== path.node ||
shouldWrapInClosure(parentPath)
);
}

const functionParent = path.getFunctionParent();

if (
!functionParent ||
parentPath.node === functionParent.node ||
parentPath.parent === functionParent.node
) {
return false;
}

if (
(parentPath.isReturnStatement() || parentPath.isExpressionStatement()) &&
parentPath.parentPath.isBlockStatement() &&
parentPath.parentPath.parent === functionParent.node
) {
const body = functionParent.get('body.body');

return !Array.isArray(body) || body.length > 1;
}

return maybeNestedConditional;
return true;
}

0 comments on commit 111cfa7

Please sign in to comment.