roqa 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/package.json +77 -0
- package/src/compiler/codegen.js +1217 -0
- package/src/compiler/index.js +47 -0
- package/src/compiler/parser.js +197 -0
- package/src/compiler/transforms/bind-detector.js +264 -0
- package/src/compiler/transforms/events.js +246 -0
- package/src/compiler/transforms/for-transform.js +164 -0
- package/src/compiler/transforms/inline-get.js +1049 -0
- package/src/compiler/transforms/jsx-to-template.js +871 -0
- package/src/compiler/transforms/show-transform.js +78 -0
- package/src/compiler/transforms/validate.js +80 -0
- package/src/compiler/utils.js +69 -0
- package/src/jsx-runtime.d.ts +640 -0
- package/src/jsx-runtime.js +73 -0
- package/src/runtime/cell.js +37 -0
- package/src/runtime/component.js +241 -0
- package/src/runtime/events.js +156 -0
- package/src/runtime/for-block.js +374 -0
- package/src/runtime/index.js +17 -0
- package/src/runtime/show-block.js +115 -0
- package/src/runtime/template.js +32 -0
- package/types/compiler.d.ts +9 -0
- package/types/index.d.ts +433 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { generateOutput } from "./codegen.js";
|
|
2
|
+
import { parse } from "./parser.js";
|
|
3
|
+
import { inlineGetCalls } from "./transforms/inline-get.js";
|
|
4
|
+
import { validateNoCustomComponents } from "./transforms/validate.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Main entry point for the Roqa JSX compiler
|
|
8
|
+
*
|
|
9
|
+
* Compilation pipeline:
|
|
10
|
+
* 1. Parse JSX source code into AST
|
|
11
|
+
* 2. Validate (no unsupported PascalCase components)
|
|
12
|
+
* 3. Generate output code with template extraction, event transforms, and bindings
|
|
13
|
+
* 4. Inline get() calls to direct property access (get(x) -> x.v)
|
|
14
|
+
*
|
|
15
|
+
* @param {string} code - Source code to compile
|
|
16
|
+
* @param {string} filename - Source filename for error messages and source maps
|
|
17
|
+
* @returns {{ code: string, map: object }} - Compiled code and source map
|
|
18
|
+
*/
|
|
19
|
+
export function compile(code, filename) {
|
|
20
|
+
// Step 1: Parse
|
|
21
|
+
const ast = parse(code, filename);
|
|
22
|
+
|
|
23
|
+
// Step 2: Validate (after parsing, before transforms)
|
|
24
|
+
// This checks for unsupported PascalCase components
|
|
25
|
+
// Note: <For> is handled specially in codegen, so it won't trigger validation error
|
|
26
|
+
validateNoCustomComponents(ast);
|
|
27
|
+
|
|
28
|
+
// Step 3: Generate output
|
|
29
|
+
// This handles:
|
|
30
|
+
// - Template extraction and deduplication
|
|
31
|
+
// - <For> component transformation
|
|
32
|
+
// - Event handler transformation
|
|
33
|
+
// - Reactive binding detection (get() -> bind())
|
|
34
|
+
const result = generateOutput(code, ast, filename);
|
|
35
|
+
|
|
36
|
+
// Step 4: Inline get() calls to direct property access
|
|
37
|
+
// This optimizes get(cell) to cell.v, avoiding function call overhead
|
|
38
|
+
const inlined = inlineGetCalls(result.code, filename);
|
|
39
|
+
|
|
40
|
+
return inlined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Re-export for direct usage if needed
|
|
44
|
+
export { parse } from "./parser.js";
|
|
45
|
+
export { validateNoCustomComponents } from "./transforms/validate.js";
|
|
46
|
+
export { generateOutput } from "./codegen.js";
|
|
47
|
+
export { inlineGetCalls } from "./transforms/inline-get.js";
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import * as parser from "@babel/parser";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse JSX source code into an AST
|
|
5
|
+
* @param {string} code - Source code to parse
|
|
6
|
+
* @param {string} filename - Source filename for error messages
|
|
7
|
+
* @returns {import("@babel/types").File} - Babel AST
|
|
8
|
+
*/
|
|
9
|
+
export function parse(code, filename) {
|
|
10
|
+
const isTypeScript = filename && (filename.endsWith(".tsx") || filename.endsWith(".ts"));
|
|
11
|
+
const plugins = isTypeScript ? ["jsx", "typescript"] : ["jsx"];
|
|
12
|
+
|
|
13
|
+
return parser.parse(code, {
|
|
14
|
+
sourceType: "module",
|
|
15
|
+
plugins,
|
|
16
|
+
sourceFilename: filename,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a JSX element is a control flow component (<For>, <Show>)
|
|
22
|
+
* @param {import("@babel/types").JSXElement} node
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function isControlFlowComponent(node) {
|
|
26
|
+
const name = getJSXElementName(node);
|
|
27
|
+
return name === "For" || name === "Show";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if a JSX element is the <For> component
|
|
32
|
+
* @param {import("@babel/types").JSXElement} node
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
export function isForComponent(node) {
|
|
36
|
+
return getJSXElementName(node) === "For";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if a JSX element is the <Show> component
|
|
41
|
+
* @param {import("@babel/types").JSXElement} node
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
export function isShowComponent(node) {
|
|
45
|
+
return getJSXElementName(node) === "Show";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a string is PascalCase (starts with uppercase)
|
|
50
|
+
* @param {string} name
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
export function isPascalCase(name) {
|
|
54
|
+
return /^[A-Z]/.test(name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the name of a JSX element
|
|
59
|
+
* @param {import("@babel/types").JSXElement} node
|
|
60
|
+
* @returns {string|null}
|
|
61
|
+
*/
|
|
62
|
+
export function getJSXElementName(node) {
|
|
63
|
+
const openingElement = node.openingElement;
|
|
64
|
+
if (!openingElement) return null;
|
|
65
|
+
|
|
66
|
+
const nameNode = openingElement.name;
|
|
67
|
+
|
|
68
|
+
// <div>, <For>, <my-component>
|
|
69
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
70
|
+
return nameNode.name;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// <Foo.Bar> - member expression
|
|
74
|
+
if (nameNode.type === "JSXMemberExpression") {
|
|
75
|
+
return getMemberExpressionName(nameNode);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the full name from a JSXMemberExpression (Foo.Bar.Baz)
|
|
83
|
+
* @param {import("@babel/types").JSXMemberExpression} node
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function getMemberExpressionName(node) {
|
|
87
|
+
const parts = [];
|
|
88
|
+
let current = node;
|
|
89
|
+
|
|
90
|
+
while (current.type === "JSXMemberExpression") {
|
|
91
|
+
parts.unshift(current.property.name);
|
|
92
|
+
current = current.object;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (current.type === "JSXIdentifier") {
|
|
96
|
+
parts.unshift(current.name);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return parts.join(".");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Extract attributes from a JSX opening element
|
|
104
|
+
* @param {import("@babel/types").JSXOpeningElement} openingElement
|
|
105
|
+
* @returns {Map<string, import("@babel/types").Node>}
|
|
106
|
+
*/
|
|
107
|
+
export function extractJSXAttributes(openingElement) {
|
|
108
|
+
const attrs = new Map();
|
|
109
|
+
|
|
110
|
+
for (const attr of openingElement.attributes) {
|
|
111
|
+
if (attr.type === "JSXAttribute") {
|
|
112
|
+
const name = attr.name.name;
|
|
113
|
+
// Value can be: StringLiteral, JSXExpressionContainer, or null (boolean true)
|
|
114
|
+
attrs.set(name, attr.value);
|
|
115
|
+
} else if (attr.type === "JSXSpreadAttribute") {
|
|
116
|
+
// Mark spread attributes specially
|
|
117
|
+
attrs.set("...", attr.argument);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return attrs;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if a node is a JSX element
|
|
126
|
+
* @param {import("@babel/types").Node} node
|
|
127
|
+
* @returns {boolean}
|
|
128
|
+
*/
|
|
129
|
+
export function isJSXElement(node) {
|
|
130
|
+
return node && node.type === "JSXElement";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if a node is a JSX fragment
|
|
135
|
+
* @param {import("@babel/types").Node} node
|
|
136
|
+
* @returns {boolean}
|
|
137
|
+
*/
|
|
138
|
+
export function isJSXFragment(node) {
|
|
139
|
+
return node && node.type === "JSXFragment";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Check if a node is JSX text
|
|
144
|
+
* @param {import("@babel/types").Node} node
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
export function isJSXText(node) {
|
|
148
|
+
return node && node.type === "JSXText";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if a node is a JSX expression container
|
|
153
|
+
* @param {import("@babel/types").Node} node
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
export function isJSXExpressionContainer(node) {
|
|
157
|
+
return node && node.type === "JSXExpressionContainer";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get children of a JSX element, filtering out empty text nodes
|
|
162
|
+
* @param {import("@babel/types").JSXElement} node
|
|
163
|
+
* @returns {import("@babel/types").Node[]}
|
|
164
|
+
*/
|
|
165
|
+
export function getJSXChildren(node) {
|
|
166
|
+
return node.children.filter((child) => {
|
|
167
|
+
// Filter out whitespace-only text nodes
|
|
168
|
+
if (child.type === "JSXText") {
|
|
169
|
+
return child.value.trim().length > 0;
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if a CallExpression is a get() call
|
|
177
|
+
* @param {import("@babel/types").Node} node
|
|
178
|
+
* @returns {boolean}
|
|
179
|
+
*/
|
|
180
|
+
export function isGetCall(node) {
|
|
181
|
+
return (
|
|
182
|
+
node &&
|
|
183
|
+
node.type === "CallExpression" &&
|
|
184
|
+
node.callee.type === "Identifier" &&
|
|
185
|
+
node.callee.name === "get"
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Extract the cell argument from a get() call
|
|
191
|
+
* @param {import("@babel/types").CallExpression} node
|
|
192
|
+
* @returns {import("@babel/types").Node|null}
|
|
193
|
+
*/
|
|
194
|
+
export function extractGetCellArg(node) {
|
|
195
|
+
if (!isGetCall(node)) return null;
|
|
196
|
+
return node.arguments[0] || null;
|
|
197
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { isGetCall, extractGetCellArg } from "../parser.js";
|
|
2
|
+
import { traverse } from "../utils.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Auto-detect get() calls in JSX expressions and generate bind() wrappers
|
|
6
|
+
*
|
|
7
|
+
* Input JSX:
|
|
8
|
+
* <tr class={get(row.isSelected) ? "danger" : ""}>{get(row.label)}</tr>
|
|
9
|
+
*
|
|
10
|
+
* Output:
|
|
11
|
+
* bind(row.isSelected, (v) => { tr_1.className = v ? "danger" : ""; });
|
|
12
|
+
* bind(row.label, (v) => { a_1_text.nodeValue = v; });
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} BindingInfo
|
|
17
|
+
* @property {import("@babel/types").Node} cellArg - The cell argument (e.g., row.label)
|
|
18
|
+
* @property {import("@babel/types").Node} fullExpression - The complete expression containing get()
|
|
19
|
+
* @property {string} targetVar - Variable name of the DOM element/text node
|
|
20
|
+
* @property {string} targetProperty - Property to update (nodeValue, className, etc.)
|
|
21
|
+
* @property {"text"|"attribute"} bindingType - Type of binding
|
|
22
|
+
* @property {string} attrName - Original attribute name (for attribute bindings)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Analyze an expression for get() calls
|
|
27
|
+
* @param {import("@babel/types").Node} expression - The expression to analyze
|
|
28
|
+
* @returns {GetCallInfo[]} - All get() calls found in the expression
|
|
29
|
+
*
|
|
30
|
+
* @typedef {Object} GetCallInfo
|
|
31
|
+
* @property {import("@babel/types").Node} cellArg - The cell reference (e.g., row.label)
|
|
32
|
+
* @property {import("@babel/types").CallExpression} callNode - The get() call node
|
|
33
|
+
* @property {boolean} isOnlyExpression - Whether this get() is the entire expression
|
|
34
|
+
*/
|
|
35
|
+
export function findGetCalls(expression) {
|
|
36
|
+
const getCalls = [];
|
|
37
|
+
|
|
38
|
+
// Simple case: expression IS a get() call
|
|
39
|
+
if (isGetCall(expression)) {
|
|
40
|
+
getCalls.push({
|
|
41
|
+
cellArg: extractGetCellArg(expression),
|
|
42
|
+
callNode: expression,
|
|
43
|
+
isOnlyExpression: true,
|
|
44
|
+
});
|
|
45
|
+
return getCalls;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Complex case: get() is somewhere inside the expression
|
|
49
|
+
// Use Babel traverse to find all get() calls
|
|
50
|
+
const visitedNodes = new Set();
|
|
51
|
+
|
|
52
|
+
// Create a mini AST wrapper for traverse
|
|
53
|
+
const wrapper = {
|
|
54
|
+
type: "Program",
|
|
55
|
+
body: [
|
|
56
|
+
{
|
|
57
|
+
type: "ExpressionStatement",
|
|
58
|
+
expression: expression,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
traverse(wrapper, {
|
|
64
|
+
CallExpression(path) {
|
|
65
|
+
const node = path.node;
|
|
66
|
+
if (visitedNodes.has(node)) return;
|
|
67
|
+
visitedNodes.add(node);
|
|
68
|
+
|
|
69
|
+
if (isGetCall(node)) {
|
|
70
|
+
getCalls.push({
|
|
71
|
+
cellArg: extractGetCellArg(node),
|
|
72
|
+
callNode: node,
|
|
73
|
+
isOnlyExpression: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
noScope: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return getCalls;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Process bindings from template extraction and generate bind() info
|
|
85
|
+
* @param {Array} bindings - Bindings from jsx-to-template
|
|
86
|
+
* @param {string} code - Original source code
|
|
87
|
+
* @returns {ProcessedBinding[]}
|
|
88
|
+
*
|
|
89
|
+
* @typedef {Object} ProcessedBinding
|
|
90
|
+
* @property {string} targetVar - Variable name of the target element
|
|
91
|
+
* @property {string} targetProperty - Property to update
|
|
92
|
+
* @property {import("@babel/types").Node} cellArg - Cell to bind to
|
|
93
|
+
* @property {import("@babel/types").Node} fullExpression - Full expression for the update callback
|
|
94
|
+
* @property {boolean} needsTransform - Whether to transform get(cell) to v in callback
|
|
95
|
+
* @property {Array} contentParts - Array of content parts (static/dynamic) for concatenated text
|
|
96
|
+
*/
|
|
97
|
+
export function processBindings(bindings, code) {
|
|
98
|
+
const processed = [];
|
|
99
|
+
|
|
100
|
+
for (const binding of bindings) {
|
|
101
|
+
const { type, varName } = binding;
|
|
102
|
+
|
|
103
|
+
// Handle prop bindings (for custom elements)
|
|
104
|
+
if (type === "prop") {
|
|
105
|
+
const { propName, expression, isStatic } = binding;
|
|
106
|
+
|
|
107
|
+
// Check if expression is a string literal (static prop)
|
|
108
|
+
if (isStatic || expression.type === "StringLiteral") {
|
|
109
|
+
processed.push({
|
|
110
|
+
type: "prop",
|
|
111
|
+
targetVar: varName,
|
|
112
|
+
propName,
|
|
113
|
+
expression: expression,
|
|
114
|
+
isStatic: true,
|
|
115
|
+
});
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Find get() calls in the expression
|
|
120
|
+
const getCalls = findGetCalls(expression);
|
|
121
|
+
|
|
122
|
+
if (getCalls.length === 0) {
|
|
123
|
+
// No get() calls - static expression
|
|
124
|
+
processed.push({
|
|
125
|
+
type: "prop",
|
|
126
|
+
targetVar: varName,
|
|
127
|
+
propName,
|
|
128
|
+
fullExpression: expression,
|
|
129
|
+
isStatic: true,
|
|
130
|
+
});
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Reactive prop
|
|
135
|
+
for (const getCall of getCalls) {
|
|
136
|
+
processed.push({
|
|
137
|
+
type: "prop",
|
|
138
|
+
targetVar: varName,
|
|
139
|
+
propName,
|
|
140
|
+
cellArg: getCall.cellArg,
|
|
141
|
+
fullExpression: expression,
|
|
142
|
+
needsTransform: !getCall.isOnlyExpression,
|
|
143
|
+
isStatic: false,
|
|
144
|
+
getCallNode: getCall.callNode,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Handle new contentParts format for text bindings
|
|
151
|
+
if (type === "text" && binding.contentParts) {
|
|
152
|
+
const { textVarName, contentParts } = binding;
|
|
153
|
+
|
|
154
|
+
// Collect all get() calls from all dynamic parts
|
|
155
|
+
const allGetCalls = [];
|
|
156
|
+
for (const part of contentParts) {
|
|
157
|
+
if (part.type === "dynamic") {
|
|
158
|
+
const getCalls = findGetCalls(part.expression);
|
|
159
|
+
for (const getCall of getCalls) {
|
|
160
|
+
allGetCalls.push({
|
|
161
|
+
...getCall,
|
|
162
|
+
partExpression: part.expression,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (allGetCalls.length === 0) {
|
|
169
|
+
// All static - shouldn't happen but handle it
|
|
170
|
+
processed.push({
|
|
171
|
+
targetVar: textVarName,
|
|
172
|
+
targetProperty: "nodeValue",
|
|
173
|
+
cellArg: null,
|
|
174
|
+
fullExpression: null,
|
|
175
|
+
contentParts,
|
|
176
|
+
needsTransform: false,
|
|
177
|
+
isStatic: true,
|
|
178
|
+
});
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Create a binding for each unique cell
|
|
183
|
+
// Track cells we've already created bindings for (by cell code, not position)
|
|
184
|
+
const seenCells = new Set();
|
|
185
|
+
|
|
186
|
+
for (const getCall of allGetCalls) {
|
|
187
|
+
// Create a unique key for this cell based on its code representation
|
|
188
|
+
// Use the actual cell code (e.g., "a", "row.label") not position,
|
|
189
|
+
// since the same cell may appear multiple times at different positions
|
|
190
|
+
const cellCode = code.slice(getCall.cellArg.start, getCall.cellArg.end);
|
|
191
|
+
|
|
192
|
+
if (seenCells.has(cellCode)) continue;
|
|
193
|
+
seenCells.add(cellCode);
|
|
194
|
+
|
|
195
|
+
processed.push({
|
|
196
|
+
targetVar: textVarName,
|
|
197
|
+
targetProperty: "nodeValue",
|
|
198
|
+
cellArg: getCall.cellArg,
|
|
199
|
+
fullExpression: null, // Not used with contentParts
|
|
200
|
+
contentParts,
|
|
201
|
+
needsTransform: true,
|
|
202
|
+
isStatic: false,
|
|
203
|
+
getCallNode: getCall.callNode,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Handle legacy single expression format and attribute bindings
|
|
210
|
+
const { expression, staticPrefix, usesMarker } = binding;
|
|
211
|
+
|
|
212
|
+
// Find all get() calls in this expression
|
|
213
|
+
const getCalls = findGetCalls(expression);
|
|
214
|
+
|
|
215
|
+
if (getCalls.length === 0) {
|
|
216
|
+
// No get() calls - this is a static expression, no binding needed
|
|
217
|
+
// But we still need to set the initial value
|
|
218
|
+
processed.push({
|
|
219
|
+
targetVar: type === "text" ? binding.textVarName : varName,
|
|
220
|
+
targetProperty: type === "text" ? "nodeValue" : binding.attrName,
|
|
221
|
+
cellArg: null,
|
|
222
|
+
fullExpression: expression,
|
|
223
|
+
needsTransform: false,
|
|
224
|
+
isStatic: true,
|
|
225
|
+
staticPrefix: staticPrefix || "",
|
|
226
|
+
usesMarker: usesMarker || false,
|
|
227
|
+
// Pass through SVG flag for proper attribute setting
|
|
228
|
+
isSvg: binding.isSvg || false,
|
|
229
|
+
});
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// For each get() call, create a binding
|
|
234
|
+
// Note: Multiple get() calls in one expression will create multiple bindings
|
|
235
|
+
// This might cause redundant updates, but ensures correctness
|
|
236
|
+
for (const getCall of getCalls) {
|
|
237
|
+
const targetProperty =
|
|
238
|
+
type === "text"
|
|
239
|
+
? "nodeValue"
|
|
240
|
+
: binding.attrName === "class" || binding.attrName === "className"
|
|
241
|
+
? "className"
|
|
242
|
+
: binding.attrName;
|
|
243
|
+
|
|
244
|
+
processed.push({
|
|
245
|
+
targetVar: type === "text" ? binding.textVarName : varName,
|
|
246
|
+
targetProperty,
|
|
247
|
+
cellArg: getCall.cellArg,
|
|
248
|
+
fullExpression: expression,
|
|
249
|
+
needsTransform: !getCall.isOnlyExpression,
|
|
250
|
+
isStatic: false,
|
|
251
|
+
// Store the get() call node for replacement in codegen
|
|
252
|
+
getCallNode: getCall.callNode,
|
|
253
|
+
// Include static prefix for text bindings
|
|
254
|
+
staticPrefix: staticPrefix || "",
|
|
255
|
+
// Pass through marker flag
|
|
256
|
+
usesMarker: usesMarker || false,
|
|
257
|
+
// Pass through SVG flag for proper attribute setting
|
|
258
|
+
isSvg: binding.isSvg || false,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return processed;
|
|
264
|
+
}
|