Skip to content

Commit

Permalink
Fix: builder shadow var 2 (#1619)
Browse files Browse the repository at this point in the history
* 🪩

* 👹

* 🪩

* 🏃

* tests

* 🪩

* 🏃

* 🪩

* 🏃

* 🪩

* 👹

* 🪩

* 👹
  • Loading branch information
samijaber authored Oct 30, 2024
1 parent 0d1decc commit a0ad5ab
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-pans-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/mitosis': patch
---

[Builder]: Fix: use scope.rename to rename identifiers in For loop
Original file line number Diff line number Diff line change
Expand Up @@ -1363,8 +1363,10 @@ export default function MyComponent(props) {
<input
type=\\"checkbox\\"
checked={todo.completed}
key={todo.id + \\"-\\" + index}
value={index}
foo={{
bar: 1,
bar: 1 + state.$index,
}}
onX={(event) => {
console.log(\\"onX\\");
Expand All @@ -1375,7 +1377,7 @@ export default function MyComponent(props) {
!state.todos[0].completed;
}}
onChange={(event) => {
console.log(\\"this is todo item: \\", state.$index);
console.log(\\"this is todo item: \\", index);
const index = state.todos.findIndex(
(t) => t.id === todo.id
);
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/__tests__/data/blocks/index-in-for.raw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ export default function MyComponent(props) {
<input
type="checkbox"
checked={todo.completed}
key={todo.id + '-' + index}
value={index}
foo={{
bar: 1,
bar: 1 + index,
}}
onX={() => {
console.log('onX');
Expand Down
79 changes: 27 additions & 52 deletions packages/core/src/generators/builder/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { hasProps } from '@/helpers/has-props';
import { isComponent } from '@/helpers/is-component';
import { isMitosisNode } from '@/helpers/is-mitosis-node';
import { isUpperCase } from '@/helpers/is-upper-case';
import { parseCode } from '@/helpers/parsers';
import { parseCodeToAst } from '@/helpers/parsers';
import { removeSurroundingBlock } from '@/helpers/remove-surrounding-block';
import { replaceNodes } from '@/helpers/replace-identifiers';
import { checkHasState } from '@/helpers/state';
Expand All @@ -17,7 +17,7 @@ import { hashCodeAsString } from '@/symbols/symbol-processor';
import { ForNode, MitosisNode } from '@/types/mitosis-node';
import { MitosisStyles } from '@/types/mitosis-styles';
import { TranspilerArgs } from '@/types/transpiler';
import { types } from '@babel/core';
import { traverse as babelTraverse, types } from '@babel/core';
import generate from '@babel/generator';
import { BuilderContent, BuilderElement } from '@builder.io/sdk';
import json5 from 'json5';
Expand Down Expand Up @@ -168,57 +168,32 @@ const componentMappers: {
if (value.type === 'single' && value.bindingType === 'function') {
try {
const code = value.code;
const processedLines: string[] = [];
let stopProcessing = false;

const newLocal = parseCode(code);

const lines =
newLocal.length === 1 && newLocal[0].type === 'BlockStatement'
? newLocal[0].body
: newLocal;

const appendSemicolonIfNeeded = (code: string) =>
code.endsWith(';') ? code : code + ';';

for (const line of lines) {
const generatedLine = appendSemicolonIfNeeded(generate(line).code);
if (stopProcessing) {
processedLines.push(generatedLine);
continue;
}

/**
* Check if this statement re-declares our `target` variable, i.e.
* if there is a variable in this function shadowing the `target` variable.
*/
let hasTargetDeclaration = false;
types.traverse(line, {
enter(path) {
if (hasTargetDeclaration) {
return;
}

if (
types.isVariableDeclarator(path) &&
types.isIdentifier(path.id) &&
path.id.name === target
) {
hasTargetDeclaration = true;
}
},
});

if (hasTargetDeclaration) {
stopProcessing = true;
processedLines.push(generatedLine);
} else {
// Replace identifiers in this statement
processedLines.push(appendSemicolonIfNeeded(replaceIndexNode(generatedLine)));
}
}

thing.bindings[key]!.code = '{' + processedLines.join('\n') + '}';
const programNode = parseCodeToAst(code);

if (!programNode) continue;

babelTraverse(programNode, {
Program(path) {
if (path.scope.hasBinding(target)) return;

const x = {
id: types.identifier(target),
init: types.identifier('PLACEHOLDER'),
};
path.scope.push(x);
path.scope.rename(target, 'state.$index');
path.traverse({
VariableDeclaration(p) {
if (p.node.declarations.length === 1 && p.node.declarations[0].id === x.id) {
p.remove();
}
},
});
},
});

thing.bindings[key]!.code = generate(programNode).code;
} catch (error) {
console.error(
'Error processing function binding. Falling back to simple replacement.',
Expand Down
15 changes: 7 additions & 8 deletions packages/core/src/helpers/parsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import decorators from '@babel/plugin-syntax-decorators';
import tsPlugin from '@babel/plugin-syntax-typescript';
import tsPreset from '@babel/preset-typescript';

export function parseCode(code: string) {
const ast = babel.parse(code, {
export function parseCodeToAst(code: string) {
return babel.parse(code, {
presets: [[tsPreset, { isTSX: true, allExtensions: true }]],
plugins: [
[tsPlugin, { isTSX: true }],
[decorators, { legacy: true }],
],
});
const body = babel.types.isFile(ast)
? ast.program.body
: babel.types.isProgram(ast)
? ast.body
: [];
return body;
}

export function parseCode(code: string) {
const ast = parseCodeToAst(code);
return babel.types.isFile(ast) ? ast.program.body : babel.types.isProgram(ast) ? ast.body : [];
}

export const isCodeBodyExpression = (body: babel.types.Statement[]) =>
Expand Down

0 comments on commit a0ad5ab

Please sign in to comment.