Skip to content

Commit 06c787f

Browse files
authored
Write-to-code for reordering elements (onlook-dev#244)
1 parent ad6f848 commit 06c787f

File tree

12 files changed

+272
-298
lines changed

12 files changed

+272
-298
lines changed

app/bun.lockb

796 Bytes
Binary file not shown.

app/common/models/code.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { TemplateNode } from './element/templateNode';
44
export interface CodeDiffRequest {
55
selector: string;
66
templateNode: TemplateNode;
7-
codeBlock: string;
87
insertedElements: InsertedElement[];
98
movedElements: MovedElementWithTemplate[];
109
attributes: Record<string, string>;
@@ -13,5 +12,5 @@ export interface CodeDiffRequest {
1312
export interface CodeDiff {
1413
original: string;
1514
generated: string;
16-
templateNode: TemplateNode;
15+
path: string;
1716
}

app/electron/main/code/diff/class.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

app/electron/main/code/diff/index.ts

Lines changed: 47 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,66 @@
1-
import generate from '@babel/generator';
2-
import traverse from '@babel/traverse';
1+
import generate, { GeneratorOptions } from '@babel/generator';
32
import * as t from '@babel/types';
3+
import { readFile } from '../files';
44
import { parseJsx, removeSemiColonIfApplicable } from '../helpers';
5-
import { getTemplateNode } from '../templateNode';
6-
import { addClassToAst } from './class';
7-
import { insertElementToAst } from './insert';
8-
import { areTemplateNodesEqual } from '/common/helpers/template';
5+
import { transformAst } from './transform';
96
import { CodeDiff, CodeDiffRequest } from '/common/models/code';
10-
import {
11-
DomActionElement,
12-
DomActionType,
13-
InsertedElement,
14-
MovedElementWithTemplate,
15-
} from '/common/models/element/domAction';
7+
import { TemplateNode } from '/common/models/element/templateNode';
168

17-
export function getCodeDiffs(requests: CodeDiffRequest[]): CodeDiff[] {
18-
const diffs: CodeDiff[] = [];
19-
const generateOptions = { retainLines: true, compact: false };
9+
interface RequestsByPath {
10+
templateToCodeDiff: Map<TemplateNode, CodeDiffRequest>;
11+
codeBlock: string;
12+
}
2013

21-
for (const request of requests) {
22-
const codeBlock = request.codeBlock;
23-
const ast = parseJsx(codeBlock);
24-
if (!ast) {
25-
continue;
26-
}
27-
const original = removeSemiColonIfApplicable(
28-
generate(ast, generateOptions, codeBlock).code,
29-
codeBlock,
30-
);
14+
export async function getCodeDiffs(
15+
templateToCodeDiff: Map<TemplateNode, CodeDiffRequest>,
16+
): Promise<CodeDiff[]> {
17+
const groupedRequests = await groupRequestsByTemplatePath(templateToCodeDiff);
18+
return processGroupedRequests(groupedRequests);
19+
}
3120

32-
if (request.attributes.className) {
33-
addClassToAst(ast, request.attributes.className);
34-
}
21+
async function groupRequestsByTemplatePath(
22+
templateToCodeDiff: Map<TemplateNode, CodeDiffRequest>,
23+
): Promise<Map<string, RequestsByPath>> {
24+
const groupedRequests: Map<string, RequestsByPath> = new Map();
3525

36-
const structureChangeElements: DomActionElement[] = [
37-
...request.insertedElements,
38-
...request.movedElements,
39-
].sort((a, b) => a.timestamp - b.timestamp);
26+
for (const [templateNode, request] of templateToCodeDiff) {
27+
const codeBlock = await readFile(templateNode.path);
28+
const path = templateNode.path;
4029

41-
for (const element of structureChangeElements) {
42-
if (element.type === DomActionType.MOVE) {
43-
// moveElementInAst(ast, element as MovedElementWithTemplate, request);
44-
} else if (element.type === DomActionType.INSERT) {
45-
insertElementToAst(ast, element as InsertedElement);
46-
}
30+
let groupedRequest = groupedRequests.get(path);
31+
if (!groupedRequest) {
32+
groupedRequest = { templateToCodeDiff: new Map(), codeBlock };
4733
}
48-
49-
const generated = removeSemiColonIfApplicable(
50-
generate(ast, generateOptions, codeBlock).code,
51-
codeBlock,
52-
);
53-
diffs.push({ original, generated, templateNode: request.templateNode });
34+
groupedRequest.templateToCodeDiff.set(templateNode, request);
35+
groupedRequests.set(path, groupedRequest);
5436
}
5537

56-
return diffs;
38+
return groupedRequests;
5739
}
5840

59-
function moveElementInAst(
60-
ast: any,
61-
element: MovedElementWithTemplate,
62-
request: CodeDiffRequest,
63-
): void {
64-
let movedNode: t.JSXElement | null = null;
65-
66-
traverse(ast, {
67-
JSXElement(path) {
68-
if (movedNode) {
69-
return;
70-
}
71-
72-
const currentTemplate = getTemplateNode(
73-
path.node,
74-
request.templateNode.path,
75-
request.templateNode.startTag.start.line,
76-
);
77-
const childTemplateNode = element.templateNode;
41+
function processGroupedRequests(groupedRequests: Map<string, RequestsByPath>): CodeDiff[] {
42+
const diffs: CodeDiff[] = [];
43+
const generateOptions: GeneratorOptions = { retainLines: true, compact: false };
7844

79-
if (areTemplateNodesEqual(currentTemplate, childTemplateNode)) {
80-
movedNode = path.node;
81-
path.remove();
82-
path.stop();
83-
}
84-
},
85-
});
45+
for (const [path, request] of groupedRequests) {
46+
const { templateToCodeDiff, codeBlock } = request;
47+
const ast = parseJsx(codeBlock);
48+
if (!ast) {
49+
continue;
50+
}
8651

87-
if (!movedNode) {
88-
console.error('Element to be moved not found');
89-
return;
52+
const original = generateCode(ast, generateOptions, codeBlock);
53+
transformAst(ast, path, templateToCodeDiff);
54+
const generated = generateCode(ast, generateOptions, codeBlock);
55+
diffs.push({ original, generated, path });
9056
}
9157

92-
let processed = false;
93-
traverse(ast, {
94-
JSXElement(path) {
95-
if (processed || !movedNode) {
96-
return;
97-
}
98-
99-
const index = element.location.index;
100-
101-
// Insert moved node into children
102-
if (!path.node.children) {
103-
path.node.children = [];
104-
}
105-
106-
if (index >= 0 && index <= path.node.children.length) {
107-
path.node.children.splice(index, 0, movedNode);
108-
} else {
109-
// If index is out of bounds, append to the end
110-
path.node.children.push(movedNode);
111-
}
112-
113-
processed = true;
114-
path.stop();
115-
},
116-
});
58+
return diffs;
59+
}
11760

118-
if (!processed) {
119-
console.error('Target location for moved element not found');
120-
}
61+
function generateCode(ast: t.File, options: GeneratorOptions, codeBlock: string): string {
62+
return removeSemiColonIfApplicable(
63+
generate(ast, { ...options, retainLines: false }, codeBlock).code,
64+
codeBlock,
65+
);
12166
}

app/electron/main/code/diff/insert.ts

Lines changed: 0 additions & 78 deletions
This file was deleted.

0 commit comments

Comments
 (0)