@novility/react-converter-core 1.0.0
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/README.md +76 -0
- package/dist/index.js +1338 -0
- package/dist/runtime/dom.js +114 -0
- package/dist/runtime/hooks.js +223 -0
- package/package.json +51 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1338 @@
|
|
|
1
|
+
// src/core/parser.js
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
function parseFile(code) {
|
|
4
|
+
return parse(code, {
|
|
5
|
+
sourceType: "module",
|
|
6
|
+
plugins: ["jsx", "typescript"]
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/core/transformer/index.js
|
|
11
|
+
import traverseModule from "@babel/traverse";
|
|
12
|
+
import * as t4 from "@babel/types";
|
|
13
|
+
|
|
14
|
+
// src/utils/ast.js
|
|
15
|
+
import * as t from "@babel/types";
|
|
16
|
+
function walkNode(node, visit) {
|
|
17
|
+
if (!node || typeof node !== "object") return;
|
|
18
|
+
if (Array.isArray(node)) {
|
|
19
|
+
node.forEach((child) => walkNode(child, visit));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
visit(node);
|
|
23
|
+
for (const key of Object.keys(node)) {
|
|
24
|
+
if (key === "loc" || key === "start" || key === "end") continue;
|
|
25
|
+
const value = node[key];
|
|
26
|
+
if (value && typeof value === "object") {
|
|
27
|
+
walkNode(value, visit);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function getJSXName(nameNode) {
|
|
32
|
+
if (t.isJSXIdentifier(nameNode)) return nameNode.name;
|
|
33
|
+
if (t.isJSXMemberExpression(nameNode)) {
|
|
34
|
+
return `${getJSXName(nameNode.object)}.${getJSXName(nameNode.property)}`;
|
|
35
|
+
}
|
|
36
|
+
return "div";
|
|
37
|
+
}
|
|
38
|
+
function isComponentTag(tagName) {
|
|
39
|
+
return Boolean(tagName && tagName[0] === tagName[0].toUpperCase());
|
|
40
|
+
}
|
|
41
|
+
function normalizeAttributeName(name) {
|
|
42
|
+
if (name === "className") return "class";
|
|
43
|
+
if (name === "htmlFor") return "for";
|
|
44
|
+
return name;
|
|
45
|
+
}
|
|
46
|
+
function normalizeText(text) {
|
|
47
|
+
return text.replace(/\s+/g, " ").trim();
|
|
48
|
+
}
|
|
49
|
+
function indent(level) {
|
|
50
|
+
return " ".repeat(level);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/core/transformer/printer.js
|
|
54
|
+
import * as t2 from "@babel/types";
|
|
55
|
+
function printExpression(expr, level = 0, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
56
|
+
if (!expr) return "undefined";
|
|
57
|
+
if (t2.isArrowFunctionExpression(expr) || t2.isFunctionExpression(expr)) {
|
|
58
|
+
return printFunctionExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
59
|
+
}
|
|
60
|
+
if (t2.isIdentifier(expr)) {
|
|
61
|
+
if (usePlaceholders && propNames && propNames.has(expr.name)) {
|
|
62
|
+
return `$${expr.name}`;
|
|
63
|
+
}
|
|
64
|
+
return expr.name;
|
|
65
|
+
}
|
|
66
|
+
if (t2.isStringLiteral(expr)) return printLiteral(expr.value);
|
|
67
|
+
if (t2.isNumericLiteral(expr)) return String(expr.value);
|
|
68
|
+
if (t2.isBooleanLiteral(expr)) return String(expr.value);
|
|
69
|
+
if (t2.isNullLiteral(expr)) return "null";
|
|
70
|
+
if (t2.isJSXElement(expr) || t2.isJSXFragment(expr)) return printJSXAsExpression2(expr, level, propNames, usePlaceholders);
|
|
71
|
+
if (typeof t2.isTSAsExpression === "function" && t2.isTSAsExpression(expr) || typeof t2.isTSTypeAssertion === "function" && t2.isTSTypeAssertion(expr)) {
|
|
72
|
+
return printExpression(expr.expression, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
73
|
+
}
|
|
74
|
+
if (typeof t2.isTSNonNullExpression === "function" && t2.isTSNonNullExpression(expr)) {
|
|
75
|
+
return printExpression(expr.expression, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
76
|
+
}
|
|
77
|
+
if (typeof t2.isTypeCastExpression === "function" && t2.isTypeCastExpression(expr)) {
|
|
78
|
+
return printExpression(expr.expression, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
79
|
+
}
|
|
80
|
+
if (t2.isCallExpression(expr)) return printCallExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
81
|
+
if (t2.isMemberExpression(expr)) return printMemberExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
82
|
+
if (typeof t2.isOptionalMemberExpression === "function" && t2.isOptionalMemberExpression(expr)) {
|
|
83
|
+
return printOptionalMemberExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
84
|
+
}
|
|
85
|
+
if (typeof t2.isOptionalCallExpression === "function" && t2.isOptionalCallExpression(expr)) {
|
|
86
|
+
return printOptionalCallExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
87
|
+
}
|
|
88
|
+
if (t2.isTemplateLiteral(expr)) return printTemplateLiteral(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
89
|
+
if (t2.isArrayExpression(expr)) return printArrayExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
90
|
+
if (t2.isObjectExpression(expr)) return printObjectExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
91
|
+
if (t2.isUnaryExpression(expr)) return `${expr.operator}${printExpression(expr.argument, level, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
92
|
+
if (t2.isUpdateExpression(expr)) {
|
|
93
|
+
return expr.prefix ? `${expr.operator}${printExpression(expr.argument, level, printJSXAsExpression2, propNames, usePlaceholders)}` : `${printExpression(expr.argument, level, printJSXAsExpression2, propNames, usePlaceholders)}${expr.operator}`;
|
|
94
|
+
}
|
|
95
|
+
if (t2.isBinaryExpression(expr) || t2.isLogicalExpression(expr)) {
|
|
96
|
+
return `${printExpression(expr.left, level, printJSXAsExpression2, propNames, usePlaceholders)} ${expr.operator} ${printExpression(expr.right, level, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
97
|
+
}
|
|
98
|
+
if (t2.isConditionalExpression(expr)) {
|
|
99
|
+
return `${printExpression(expr.test, level, printJSXAsExpression2, propNames, usePlaceholders)} ? ${printExpression(expr.consequent, level, printJSXAsExpression2, propNames, usePlaceholders)} : ${printExpression(expr.alternate, level, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
100
|
+
}
|
|
101
|
+
return "undefined";
|
|
102
|
+
}
|
|
103
|
+
function printFunctionExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
104
|
+
const params = expr.params.map((param) => printPattern(param, printJSXAsExpression2, propNames, usePlaceholders)).join(", ");
|
|
105
|
+
if (t2.isBlockStatement(expr.body)) {
|
|
106
|
+
const bodyLines = printStatementLines(expr.body.body, level + 1, printJSXAsExpression2, propNames, usePlaceholders);
|
|
107
|
+
return `function (${params}) {
|
|
108
|
+
${bodyLines.join("\n")}
|
|
109
|
+
${indent(level)}}`;
|
|
110
|
+
}
|
|
111
|
+
return `function (${params}) { return ${printExpression(expr.body, level, printJSXAsExpression2, propNames, usePlaceholders)}; }`;
|
|
112
|
+
}
|
|
113
|
+
function printPattern(param, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
114
|
+
if (t2.isIdentifier(param)) {
|
|
115
|
+
if (usePlaceholders && propNames && propNames.has(param.name)) {
|
|
116
|
+
return `$${param.name}`;
|
|
117
|
+
}
|
|
118
|
+
return param.name;
|
|
119
|
+
}
|
|
120
|
+
if (t2.isAssignmentPattern(param)) {
|
|
121
|
+
return `${printPattern(param.left, printJSXAsExpression2, propNames, usePlaceholders)} = ${printExpression(param.right, 0, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
122
|
+
}
|
|
123
|
+
if (t2.isArrayPattern(param)) {
|
|
124
|
+
const parts = param.elements.map((el) => el ? printPattern(el, printJSXAsExpression2, propNames, usePlaceholders) : "");
|
|
125
|
+
return `[${parts.join(", ")}]`;
|
|
126
|
+
}
|
|
127
|
+
if (t2.isObjectPattern(param)) {
|
|
128
|
+
const props = param.properties.map((prop) => {
|
|
129
|
+
if (t2.isObjectProperty(prop)) {
|
|
130
|
+
const key = t2.isIdentifier(prop.key) ? prop.key.name : printExpression(prop.key, 0, printJSXAsExpression2, propNames, usePlaceholders);
|
|
131
|
+
const value = printPattern(prop.value, printJSXAsExpression2, propNames, usePlaceholders);
|
|
132
|
+
if (prop.shorthand && t2.isIdentifier(prop.value) && prop.value.name === key) {
|
|
133
|
+
return key;
|
|
134
|
+
}
|
|
135
|
+
return `${key}: ${value}`;
|
|
136
|
+
}
|
|
137
|
+
if (t2.isRestElement(prop)) {
|
|
138
|
+
return `...${printPattern(prop.argument, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
139
|
+
}
|
|
140
|
+
return "";
|
|
141
|
+
});
|
|
142
|
+
return `{ ${props.filter(Boolean).join(", ")} }`;
|
|
143
|
+
}
|
|
144
|
+
if (t2.isRestElement(param)) return `...${printPattern(param.argument, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
145
|
+
return "param";
|
|
146
|
+
}
|
|
147
|
+
function printStatementLines(statements, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
148
|
+
if (!statements || statements.length === 0) return [];
|
|
149
|
+
const lines = [];
|
|
150
|
+
for (const stmt of statements) {
|
|
151
|
+
if (t2.isExpressionStatement(stmt)) {
|
|
152
|
+
lines.push(`${indent(level)}${printExpression(stmt.expression, level, printJSXAsExpression2, propNames, usePlaceholders)};`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (t2.isReturnStatement(stmt)) {
|
|
156
|
+
lines.push(`${indent(level)}return ${printExpression(stmt.argument, level, printJSXAsExpression2, propNames, usePlaceholders)};`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (t2.isVariableDeclaration(stmt)) {
|
|
160
|
+
const decls = stmt.declarations.map((decl) => {
|
|
161
|
+
const name = printPattern(decl.id, printJSXAsExpression2, propNames, usePlaceholders);
|
|
162
|
+
const init = decl.init ? ` = ${printExpression(decl.init, level, printJSXAsExpression2, propNames, usePlaceholders)}` : "";
|
|
163
|
+
return `${name}${init}`;
|
|
164
|
+
});
|
|
165
|
+
lines.push(`${indent(level)}${stmt.kind} ${decls.join(", ")};`);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (t2.isIfStatement(stmt)) {
|
|
169
|
+
lines.push(`${indent(level)}if (${printExpression(stmt.test, level, printJSXAsExpression2, propNames, usePlaceholders)}) {`);
|
|
170
|
+
lines.push(...printStatementLines(asStatementList(stmt.consequent), level + 1, printJSXAsExpression2, propNames, usePlaceholders));
|
|
171
|
+
lines.push(`${indent(level)}}`);
|
|
172
|
+
if (stmt.alternate) {
|
|
173
|
+
if (t2.isIfStatement(stmt.alternate)) {
|
|
174
|
+
lines.push(`${indent(level)}else ` + printInlineIf(stmt.alternate, printJSXAsExpression2, propNames, usePlaceholders));
|
|
175
|
+
} else {
|
|
176
|
+
lines.push(`${indent(level)}else {`);
|
|
177
|
+
lines.push(...printStatementLines(asStatementList(stmt.alternate), level + 1, printJSXAsExpression2, propNames, usePlaceholders));
|
|
178
|
+
lines.push(`${indent(level)}}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (t2.isSwitchStatement(stmt)) {
|
|
184
|
+
lines.push(`${indent(level)}switch (${printExpression(stmt.discriminant, level, printJSXAsExpression2, propNames, usePlaceholders)}) {`);
|
|
185
|
+
for (const switchCase of stmt.cases) {
|
|
186
|
+
const label = switchCase.test ? `case ${printExpression(switchCase.test, level, printJSXAsExpression2, propNames, usePlaceholders)}:` : "default:";
|
|
187
|
+
lines.push(`${indent(level + 1)}${label}`);
|
|
188
|
+
lines.push(...printStatementLines(switchCase.consequent, level + 2, printJSXAsExpression2, propNames, usePlaceholders));
|
|
189
|
+
}
|
|
190
|
+
lines.push(`${indent(level)}}`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (t2.isBreakStatement(stmt)) {
|
|
194
|
+
lines.push(`${indent(level)}break;`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (t2.isFunctionDeclaration(stmt) && stmt.id) {
|
|
198
|
+
const name = stmt.id.name;
|
|
199
|
+
const params = stmt.params.map((param) => printPattern(param, printJSXAsExpression2, propNames, usePlaceholders)).join(", ");
|
|
200
|
+
const bodyLines = printStatementLines(stmt.body.body, level + 1, printJSXAsExpression2, propNames, usePlaceholders);
|
|
201
|
+
lines.push(`${indent(level)}function ${name}(${params}) {`);
|
|
202
|
+
lines.push(...bodyLines);
|
|
203
|
+
lines.push(`${indent(level)}}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return lines;
|
|
207
|
+
}
|
|
208
|
+
function asStatementList(stmt) {
|
|
209
|
+
if (t2.isBlockStatement(stmt)) return stmt.body;
|
|
210
|
+
return [stmt];
|
|
211
|
+
}
|
|
212
|
+
function printInlineIf(stmt, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
213
|
+
const test = printExpression(stmt.test, 0, printJSXAsExpression2, propNames, usePlaceholders);
|
|
214
|
+
const consequentLines = printStatementLines(asStatementList(stmt.consequent), 0, printJSXAsExpression2, propNames, usePlaceholders);
|
|
215
|
+
const consequentBody = consequentLines.length ? `if (${test}) {
|
|
216
|
+
${consequentLines.join("\n")}
|
|
217
|
+
}` : `if (${test}) {}`;
|
|
218
|
+
if (!stmt.alternate) return consequentBody;
|
|
219
|
+
if (t2.isIfStatement(stmt.alternate)) {
|
|
220
|
+
return `${consequentBody} else ${printInlineIf(stmt.alternate, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
221
|
+
}
|
|
222
|
+
const alternateLines = printStatementLines(asStatementList(stmt.alternate), 0, printJSXAsExpression2, propNames, usePlaceholders);
|
|
223
|
+
const alternateBody = alternateLines.length ? ` else {
|
|
224
|
+
${alternateLines.join("\n")}
|
|
225
|
+
}` : " else {}";
|
|
226
|
+
return `${consequentBody}${alternateBody}`;
|
|
227
|
+
}
|
|
228
|
+
function printCallExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
229
|
+
const callee = printExpression(expr.callee, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
230
|
+
const args = expr.arguments.map((arg) => printExpression(arg, level, printJSXAsExpression2, propNames, usePlaceholders)).join(", ");
|
|
231
|
+
return `${callee}(${args})`;
|
|
232
|
+
}
|
|
233
|
+
function printMemberExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
234
|
+
const object = printExpression(expr.object, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
235
|
+
const prop = expr.computed ? `[${printExpression(expr.property, level, printJSXAsExpression2, propNames, usePlaceholders)}]` : `.${expr.property.name}`;
|
|
236
|
+
return `${object}${prop}`;
|
|
237
|
+
}
|
|
238
|
+
function printOptionalMemberExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
239
|
+
const object = printExpression(expr.object, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
240
|
+
const prop = expr.computed ? `[${printExpression(expr.property, level, printJSXAsExpression2, propNames, usePlaceholders)}]` : `.${expr.property.name}`;
|
|
241
|
+
return `${object}?.${prop.startsWith(".") ? prop.slice(1) : prop}`;
|
|
242
|
+
}
|
|
243
|
+
function printOptionalCallExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
244
|
+
const callee = printExpression(expr.callee, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
245
|
+
const args = expr.arguments.map((arg) => printExpression(arg, level, printJSXAsExpression2, propNames, usePlaceholders)).join(", ");
|
|
246
|
+
return `${callee}?.(${args})`;
|
|
247
|
+
}
|
|
248
|
+
function printTemplateLiteral(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
249
|
+
const parts = expr.quasis.map((q) => q.value.cooked || "");
|
|
250
|
+
if (expr.expressions.length === 0) {
|
|
251
|
+
return printLiteral(parts.join(""));
|
|
252
|
+
}
|
|
253
|
+
const expressions = expr.expressions.map((e) => printExpression(e, level, printJSXAsExpression2, propNames, usePlaceholders));
|
|
254
|
+
let output = "`";
|
|
255
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
256
|
+
output += escapeTemplateChunk(parts[i]);
|
|
257
|
+
if (expressions[i]) output += "${" + expressions[i] + "}";
|
|
258
|
+
}
|
|
259
|
+
output += "`";
|
|
260
|
+
return output;
|
|
261
|
+
}
|
|
262
|
+
function escapeTemplateChunk(value) {
|
|
263
|
+
return String(value).replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
264
|
+
}
|
|
265
|
+
function printArrayExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
266
|
+
const parts = expr.elements.map((el) => {
|
|
267
|
+
if (!el) return "";
|
|
268
|
+
if (t2.isSpreadElement(el)) return `...${printExpression(el.argument, level, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
269
|
+
return printExpression(el, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
270
|
+
});
|
|
271
|
+
return `[${parts.join(", ")}]`;
|
|
272
|
+
}
|
|
273
|
+
function printObjectExpression(expr, level, printJSXAsExpression2, propNames, usePlaceholders) {
|
|
274
|
+
const props = expr.properties.map((prop) => {
|
|
275
|
+
if (t2.isObjectProperty(prop)) {
|
|
276
|
+
const key = t2.isIdentifier(prop.key) ? prop.key.name : printExpression(prop.key, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
277
|
+
const value = printExpression(prop.value, level, printJSXAsExpression2, propNames, usePlaceholders);
|
|
278
|
+
if (prop.shorthand && t2.isIdentifier(prop.value) && prop.value.name === key) {
|
|
279
|
+
return key;
|
|
280
|
+
}
|
|
281
|
+
return `${key}: ${value}`;
|
|
282
|
+
}
|
|
283
|
+
if (t2.isSpreadElement(prop)) {
|
|
284
|
+
return `...${printExpression(prop.argument, level, printJSXAsExpression2, propNames, usePlaceholders)}`;
|
|
285
|
+
}
|
|
286
|
+
return "";
|
|
287
|
+
});
|
|
288
|
+
return `{ ${props.filter(Boolean).join(", ")} }`;
|
|
289
|
+
}
|
|
290
|
+
function printLiteral(value, isTemplate = false) {
|
|
291
|
+
const base = String(value).replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/\r/g, "").replace(/\t/g, "\\t");
|
|
292
|
+
if (isTemplate) {
|
|
293
|
+
const escaped2 = base.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
294
|
+
return `\`${escaped2}\``;
|
|
295
|
+
}
|
|
296
|
+
const escaped = base.replace(/"/g, '\\"');
|
|
297
|
+
return `"${escaped}"`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/core/transformer/jsx-emitter.js
|
|
301
|
+
import * as t3 from "@babel/types";
|
|
302
|
+
function emitJSX(node, varName, level, lines, propNames, usePlaceholders) {
|
|
303
|
+
if (t3.isJSXFragment(node)) {
|
|
304
|
+
lines.push(`${indent(level)}const ${varName} = document.createDocumentFragment();`);
|
|
305
|
+
emitChildren(node.children, varName, level, lines, propNames, usePlaceholders);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (!t3.isJSXElement(node)) return;
|
|
309
|
+
const tagName = getJSXName(node.openingElement.name);
|
|
310
|
+
if (isComponentTag(tagName)) {
|
|
311
|
+
const propsExpr = buildPropsExpression(node.openingElement.attributes, node.children, level, propNames, usePlaceholders);
|
|
312
|
+
lines.push(`${indent(level)}const ${varName} = __renderComponent(${tagName}, ${propsExpr});`);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
lines.push(`${indent(level)}const ${varName} = document.createElement("${tagName}");`);
|
|
316
|
+
for (const attr of node.openingElement.attributes) {
|
|
317
|
+
if (t3.isJSXSpreadAttribute(attr)) continue;
|
|
318
|
+
if (!t3.isJSXIdentifier(attr.name)) continue;
|
|
319
|
+
const rawName = attr.name.name;
|
|
320
|
+
const attrName = normalizeAttributeName(rawName);
|
|
321
|
+
if (rawName.startsWith("on") && rawName.length > 2) {
|
|
322
|
+
const event = rawName.slice(2).toLowerCase();
|
|
323
|
+
const handler = attr.value ? printExpression(getExpressionValue(attr.value), level, printJSXAsExpression, propNames, usePlaceholders) : "function(){}";
|
|
324
|
+
lines.push(`${indent(level)}__addEventListener(${varName}, "${event}", ${handler});`);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (!attr.value) {
|
|
328
|
+
lines.push(`${indent(level)}${varName}.setAttribute("${attrName}", "");`);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (t3.isStringLiteral(attr.value)) {
|
|
332
|
+
if (attrName === "style") {
|
|
333
|
+
lines.push(
|
|
334
|
+
`${indent(level)}__setStyle(${varName}, ${printLiteral(attr.value.value)});`
|
|
335
|
+
);
|
|
336
|
+
} else {
|
|
337
|
+
lines.push(
|
|
338
|
+
`${indent(level)}__setAttribute(${varName}, "${attrName}", ${printLiteral(attr.value.value)});`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (t3.isJSXExpressionContainer(attr.value)) {
|
|
344
|
+
const expr = attr.value.expression;
|
|
345
|
+
if (attrName === "style") {
|
|
346
|
+
lines.push(`${indent(level)}__setStyle(${varName}, ${printExpression(expr, level, printJSXAsExpression, propNames, usePlaceholders)});`);
|
|
347
|
+
} else {
|
|
348
|
+
lines.push(
|
|
349
|
+
`${indent(level)}__setAttribute(${varName}, "${attrName}", ${printExpression(expr, level, printJSXAsExpression, propNames, usePlaceholders)});`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
emitChildren(node.children, varName, level, lines, propNames, usePlaceholders);
|
|
355
|
+
}
|
|
356
|
+
function emitChildren(children, varName, level, lines, propNames, usePlaceholders) {
|
|
357
|
+
let index = 0;
|
|
358
|
+
for (const child of children) {
|
|
359
|
+
const childVar = `${varName}_${index}`;
|
|
360
|
+
if (t3.isJSXText(child)) {
|
|
361
|
+
const text = normalizeText(child.value);
|
|
362
|
+
if (text) {
|
|
363
|
+
lines.push(`${indent(level)}__appendChild(${varName}, ${printLiteral(text)});`);
|
|
364
|
+
}
|
|
365
|
+
index += 1;
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (t3.isJSXElement(child) || t3.isJSXFragment(child)) {
|
|
369
|
+
emitJSX(child, childVar, level, lines, propNames, usePlaceholders);
|
|
370
|
+
lines.push(`${indent(level)}__appendChild(${varName}, ${childVar});`);
|
|
371
|
+
index += 1;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (t3.isJSXExpressionContainer(child)) {
|
|
375
|
+
const expr = child.expression;
|
|
376
|
+
if (!t3.isJSXEmptyExpression(expr)) {
|
|
377
|
+
lines.push(`${indent(level)}__appendChild(${varName}, ${printExpression(expr, level, printJSXAsExpression, propNames, usePlaceholders)});`);
|
|
378
|
+
}
|
|
379
|
+
index += 1;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function buildPropsExpression(attributes, children, level, propNames, usePlaceholders) {
|
|
384
|
+
const props = [];
|
|
385
|
+
for (const attr of attributes) {
|
|
386
|
+
if (t3.isJSXSpreadAttribute(attr)) {
|
|
387
|
+
props.push(`...${printExpression(attr.argument, level, printJSXAsExpression, propNames, usePlaceholders)}`);
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (!t3.isJSXIdentifier(attr.name)) continue;
|
|
391
|
+
const name = attr.name.name;
|
|
392
|
+
if (!attr.value) {
|
|
393
|
+
props.push(`${name}: true`);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (t3.isStringLiteral(attr.value)) {
|
|
397
|
+
props.push(`${name}: ${printLiteral(attr.value.value)}`);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (t3.isJSXExpressionContainer(attr.value)) {
|
|
401
|
+
const expr = attr.value.expression;
|
|
402
|
+
if (!t3.isJSXEmptyExpression(expr)) {
|
|
403
|
+
props.push(`${name}: ${printExpression(expr, level, printJSXAsExpression, propNames, usePlaceholders)}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const childrenExpr = buildChildrenProp(children, level, propNames, usePlaceholders);
|
|
408
|
+
if (childrenExpr) {
|
|
409
|
+
props.push(`children: ${childrenExpr}`);
|
|
410
|
+
}
|
|
411
|
+
if (props.length === 0) return "{}";
|
|
412
|
+
return `{ ${props.join(", ")} }`;
|
|
413
|
+
}
|
|
414
|
+
function buildChildrenProp(children, level, propNames, usePlaceholders) {
|
|
415
|
+
const childExprs = [];
|
|
416
|
+
for (const child of children) {
|
|
417
|
+
const expr = printChildExpression(child, level, propNames, usePlaceholders);
|
|
418
|
+
if (expr) childExprs.push(expr);
|
|
419
|
+
}
|
|
420
|
+
if (childExprs.length === 0) return "";
|
|
421
|
+
if (childExprs.length === 1) return childExprs[0];
|
|
422
|
+
return `[${childExprs.join(", ")}]`;
|
|
423
|
+
}
|
|
424
|
+
function printChildExpression(child, level, propNames, usePlaceholders) {
|
|
425
|
+
if (t3.isJSXText(child)) {
|
|
426
|
+
const text = normalizeText(child.value);
|
|
427
|
+
return text ? printLiteral(text) : "";
|
|
428
|
+
}
|
|
429
|
+
if (t3.isJSXElement(child) || t3.isJSXFragment(child)) {
|
|
430
|
+
return printJSXAsExpression(child, level, propNames, usePlaceholders);
|
|
431
|
+
}
|
|
432
|
+
if (t3.isJSXExpressionContainer(child)) {
|
|
433
|
+
const expr = child.expression;
|
|
434
|
+
if (t3.isJSXEmptyExpression(expr)) return "";
|
|
435
|
+
return printExpression(expr, level, printJSXAsExpression, propNames, usePlaceholders);
|
|
436
|
+
}
|
|
437
|
+
return "";
|
|
438
|
+
}
|
|
439
|
+
function printJSXAsExpression(node, level, propNames, usePlaceholders) {
|
|
440
|
+
const lines = [];
|
|
441
|
+
emitJSX(node, "jsx_node", level + 1, lines, propNames, usePlaceholders);
|
|
442
|
+
lines.push(`${indent(level + 1)}return jsx_node;`);
|
|
443
|
+
return `(() => {
|
|
444
|
+
${lines.join("\n")}
|
|
445
|
+
${indent(level)}})()`;
|
|
446
|
+
}
|
|
447
|
+
function getExpressionValue(valueNode) {
|
|
448
|
+
if (t3.isJSXExpressionContainer(valueNode)) {
|
|
449
|
+
return valueNode.expression;
|
|
450
|
+
}
|
|
451
|
+
if (t3.isStringLiteral(valueNode)) {
|
|
452
|
+
return valueNode;
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/core/transformer/index.js
|
|
458
|
+
var traverse = traverseModule.default ?? traverseModule;
|
|
459
|
+
var HOOK_NAMES = /* @__PURE__ */ new Set([
|
|
460
|
+
"useState",
|
|
461
|
+
"useEffect",
|
|
462
|
+
"useRef",
|
|
463
|
+
"useReducer",
|
|
464
|
+
"useMemo",
|
|
465
|
+
"useCallback",
|
|
466
|
+
"useContext"
|
|
467
|
+
]);
|
|
468
|
+
function transform(ast, options = {}) {
|
|
469
|
+
const output = [];
|
|
470
|
+
const usePlaceholders = Boolean(options.usePlaceholders);
|
|
471
|
+
function addComponent(name, jsxNode, preStatements = [], hooksUsed2 = [], params = "", propNames = /* @__PURE__ */ new Set()) {
|
|
472
|
+
if (!name || !jsxNode) return;
|
|
473
|
+
const bodyLines = buildComponentBody(jsxNode, preStatements, propNames, usePlaceholders);
|
|
474
|
+
if (bodyLines.length === 0) return;
|
|
475
|
+
output.push({ name, bodyLines, hooksUsed: hooksUsed2, params });
|
|
476
|
+
}
|
|
477
|
+
traverse(ast, {
|
|
478
|
+
FunctionDeclaration(path2) {
|
|
479
|
+
const name = path2.node.id?.name;
|
|
480
|
+
const result = findReturnedJSX(path2.node);
|
|
481
|
+
const hooksUsed2 = collectHookNames(path2.node);
|
|
482
|
+
const params = printFunctionParams(path2.node);
|
|
483
|
+
const propNames = collectPropNames(path2.node);
|
|
484
|
+
addComponent(name, result?.jsxNode, result?.preStatements, hooksUsed2, params, propNames);
|
|
485
|
+
},
|
|
486
|
+
VariableDeclarator(path2) {
|
|
487
|
+
if (!t4.isIdentifier(path2.node.id)) return;
|
|
488
|
+
const name = path2.node.id.name;
|
|
489
|
+
const init = unwrapComponentFunction(path2.node.init);
|
|
490
|
+
if (!init) return;
|
|
491
|
+
const result = findReturnedJSX(init);
|
|
492
|
+
const hooksUsed2 = collectHookNames(init);
|
|
493
|
+
const params = printFunctionParams(init);
|
|
494
|
+
const propNames = collectPropNames(init);
|
|
495
|
+
addComponent(name, result?.jsxNode, result?.preStatements, hooksUsed2, params, propNames);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
const hooksUsed = /* @__PURE__ */ new Set();
|
|
499
|
+
for (const comp of output) {
|
|
500
|
+
if (!comp || !Array.isArray(comp.hooksUsed)) continue;
|
|
501
|
+
for (const hook of comp.hooksUsed) hooksUsed.add(hook);
|
|
502
|
+
}
|
|
503
|
+
return { components: output, hooksUsed: Array.from(hooksUsed) };
|
|
504
|
+
}
|
|
505
|
+
function unwrapComponentFunction(node) {
|
|
506
|
+
if (t4.isArrowFunctionExpression(node) || t4.isFunctionExpression(node)) {
|
|
507
|
+
return node;
|
|
508
|
+
}
|
|
509
|
+
if (t4.isCallExpression(node)) {
|
|
510
|
+
const callee = node.callee;
|
|
511
|
+
const isMemo = t4.isMemberExpression(callee) && t4.isIdentifier(callee.object, { name: "React" }) && t4.isIdentifier(callee.property, { name: "memo" }) || t4.isIdentifier(callee, { name: "memo" });
|
|
512
|
+
const firstArg = node.arguments[0];
|
|
513
|
+
if (isMemo && (t4.isArrowFunctionExpression(firstArg) || t4.isFunctionExpression(firstArg))) {
|
|
514
|
+
return firstArg;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
function printFunctionParams(fnNode) {
|
|
520
|
+
if (!fnNode || !Array.isArray(fnNode.params)) return "";
|
|
521
|
+
return fnNode.params.map((param) => printPattern(param, printJSXAsExpression)).join(", ");
|
|
522
|
+
}
|
|
523
|
+
function findReturnedJSX(fnNode) {
|
|
524
|
+
const preStatements = [];
|
|
525
|
+
if (t4.isArrowFunctionExpression(fnNode)) {
|
|
526
|
+
if (t4.isJSXElement(fnNode.body) || t4.isJSXFragment(fnNode.body)) {
|
|
527
|
+
return { jsxNode: fnNode.body, preStatements };
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (t4.isFunctionDeclaration(fnNode) || t4.isFunctionExpression(fnNode) || t4.isArrowFunctionExpression(fnNode)) {
|
|
531
|
+
if (!t4.isBlockStatement(fnNode.body)) return null;
|
|
532
|
+
for (const stmt of fnNode.body.body) {
|
|
533
|
+
if (t4.isReturnStatement(stmt)) {
|
|
534
|
+
const arg = stmt.argument;
|
|
535
|
+
if (t4.isJSXElement(arg) || t4.isJSXFragment(arg)) {
|
|
536
|
+
return { jsxNode: arg, preStatements };
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
preStatements.push(stmt);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
function buildComponentBody(jsxNode, preStatements, propNames, usePlaceholders) {
|
|
546
|
+
const lines = [];
|
|
547
|
+
lines.push(...printStatementLines(preStatements, 1, printJSXAsExpression, propNames, usePlaceholders));
|
|
548
|
+
emitJSX(jsxNode, "root", 1, lines, propNames, usePlaceholders);
|
|
549
|
+
lines.push(`${indent(1)}return root;`);
|
|
550
|
+
return lines;
|
|
551
|
+
}
|
|
552
|
+
function collectHookNames(fnNode) {
|
|
553
|
+
const hooks = /* @__PURE__ */ new Set();
|
|
554
|
+
walkNode(fnNode, (node) => {
|
|
555
|
+
if (t4.isCallExpression(node) && t4.isIdentifier(node.callee)) {
|
|
556
|
+
if (HOOK_NAMES.has(node.callee.name)) {
|
|
557
|
+
hooks.add(node.callee.name);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (t4.isCallExpression(node) && t4.isMemberExpression(node.callee)) {
|
|
561
|
+
const prop = node.callee.property;
|
|
562
|
+
if (t4.isIdentifier(prop) && HOOK_NAMES.has(prop.name)) {
|
|
563
|
+
hooks.add(prop.name);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
return Array.from(hooks);
|
|
568
|
+
}
|
|
569
|
+
function collectPropNames(fnNode) {
|
|
570
|
+
const names = /* @__PURE__ */ new Set();
|
|
571
|
+
if (!fnNode || !Array.isArray(fnNode.params)) return names;
|
|
572
|
+
for (const param of fnNode.params) {
|
|
573
|
+
if (t4.isIdentifier(param)) {
|
|
574
|
+
names.add(param.name);
|
|
575
|
+
} else if (t4.isObjectPattern(param)) {
|
|
576
|
+
for (const prop of param.properties) {
|
|
577
|
+
if (t4.isObjectProperty(prop)) {
|
|
578
|
+
if (t4.isIdentifier(prop.key)) {
|
|
579
|
+
names.add(prop.key.name);
|
|
580
|
+
}
|
|
581
|
+
} else if (t4.isRestElement(prop) && t4.isIdentifier(prop.argument)) {
|
|
582
|
+
names.add(prop.argument.name);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return names;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/utils/naming.js
|
|
591
|
+
function toKebabCase(value) {
|
|
592
|
+
return String(value).replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase();
|
|
593
|
+
}
|
|
594
|
+
function toComponentClassName(tagName, fallbackName) {
|
|
595
|
+
const base = fallbackName || tagName || "";
|
|
596
|
+
const parts = String(base).replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
597
|
+
let name = parts.map((part) => part[0].toUpperCase() + part.slice(1)).join("");
|
|
598
|
+
if (!name) name = "WebComponent";
|
|
599
|
+
if (!/^[A-Za-z_$]/.test(name)) name = `Component${name}`;
|
|
600
|
+
return `${name}Element`;
|
|
601
|
+
}
|
|
602
|
+
function resolveWebComponentTag(tag, fileName, componentName) {
|
|
603
|
+
if (tag && typeof tag === "string" && tag.includes("-")) {
|
|
604
|
+
return tag.toLowerCase();
|
|
605
|
+
}
|
|
606
|
+
const base = tag || componentName || String(fileName).split(/[\\/]/).pop().replace(/\.(tsx|jsx|ts|js)$/i, "");
|
|
607
|
+
const kebab = toKebabCase(base);
|
|
608
|
+
return kebab.includes("-") ? kebab : `x-${kebab}`;
|
|
609
|
+
}
|
|
610
|
+
function toTemplateLiteral(value) {
|
|
611
|
+
const escaped = String(value).replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
612
|
+
return `\`${escaped}\``;
|
|
613
|
+
}
|
|
614
|
+
function createStyleId(relPath) {
|
|
615
|
+
return `tw-${relPath.replace(/[^a-zA-Z0-9-_]/g, "-")}`;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/runtime/dom.js
|
|
619
|
+
var domRuntime = `
|
|
620
|
+
function __appendChild(parent, child) {
|
|
621
|
+
if (child === null || child === undefined || child === false || child === true) return;
|
|
622
|
+
if (Array.isArray(child)) {
|
|
623
|
+
for (const item of child) __appendChild(parent, item);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const isNode = child && (child.nodeType || (typeof Node !== "undefined" && child instanceof Node));
|
|
627
|
+
if (isNode) {
|
|
628
|
+
parent.appendChild(child);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
parent.appendChild(document.createTextNode(String(child)));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function __renderInstance(instance) {
|
|
635
|
+
const nextRoot = instance.component(instance.props || {});
|
|
636
|
+
if (instance.root && instance.root.parentNode) {
|
|
637
|
+
instance.root.parentNode.replaceChild(nextRoot, instance.root);
|
|
638
|
+
} else {
|
|
639
|
+
instance.mountTarget.appendChild(nextRoot);
|
|
640
|
+
}
|
|
641
|
+
instance.root = nextRoot;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function __mount(component, mountTarget, props) {
|
|
645
|
+
const instance = {
|
|
646
|
+
component,
|
|
647
|
+
props: props || {},
|
|
648
|
+
root: null,
|
|
649
|
+
mountTarget: mountTarget || document.body,
|
|
650
|
+
};
|
|
651
|
+
__renderInstance(instance);
|
|
652
|
+
return instance;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function __setAttribute(el, name, value) {
|
|
656
|
+
if (value === null || value === undefined || value === false) return;
|
|
657
|
+
el.setAttribute(name, String(value));
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function __setStyle(el, value) {
|
|
661
|
+
if (!value) return;
|
|
662
|
+
if (typeof value === "string") {
|
|
663
|
+
el.setAttribute("style", value);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (typeof value !== "object") return;
|
|
667
|
+
for (const key of Object.keys(value)) {
|
|
668
|
+
const styleValue = value[key];
|
|
669
|
+
if (styleValue === null || styleValue === undefined) continue;
|
|
670
|
+
if (key.includes("-")) {
|
|
671
|
+
el.style.setProperty(key, String(styleValue));
|
|
672
|
+
} else {
|
|
673
|
+
el.style[key] = String(styleValue);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function __addEventListener(el, event, handler) {
|
|
679
|
+
if (typeof handler !== "function") return;
|
|
680
|
+
el.addEventListener(event, handler);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function __renderComponent(component, props) {
|
|
684
|
+
return component(props || {});
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function __toCamelCase(name) {
|
|
688
|
+
return String(name).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function __getElementProps(el, lightChildren) {
|
|
692
|
+
const props = {};
|
|
693
|
+
if (!el || !el.attributes) return props;
|
|
694
|
+
for (const attr of Array.from(el.attributes)) {
|
|
695
|
+
if (attr.name === 'data-props') continue;
|
|
696
|
+
const key = __toCamelCase(attr.name);
|
|
697
|
+
props[key] = attr.value;
|
|
698
|
+
}
|
|
699
|
+
const dataProps = el.getAttribute && el.getAttribute('data-props');
|
|
700
|
+
if (dataProps) {
|
|
701
|
+
try {
|
|
702
|
+
const parsed = JSON.parse(dataProps);
|
|
703
|
+
if (parsed && typeof parsed === 'object') {
|
|
704
|
+
Object.assign(props, parsed);
|
|
705
|
+
}
|
|
706
|
+
} catch (_) {}
|
|
707
|
+
}
|
|
708
|
+
if (lightChildren && lightChildren.length) {
|
|
709
|
+
props.children = lightChildren;
|
|
710
|
+
}
|
|
711
|
+
return props;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function __getRootProps() {
|
|
715
|
+
if (typeof window !== "undefined" && window.__COMPILER_PROPS__) {
|
|
716
|
+
return window.__COMPILER_PROPS__ || {};
|
|
717
|
+
}
|
|
718
|
+
return {};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function __applyShadowStyles(cssText, styleId, shadowRoot, host) {
|
|
722
|
+
if (!cssText || !shadowRoot) return;
|
|
723
|
+
if (host && host.__styleSheet) return;
|
|
724
|
+
if (typeof injectStyles === "function") {
|
|
725
|
+
injectStyles(cssText, styleId, shadowRoot);
|
|
726
|
+
if (host) host.__styleSheet = true;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
`;
|
|
730
|
+
|
|
731
|
+
// src/runtime/hooks.js
|
|
732
|
+
function getHookRuntime(hooksUsed) {
|
|
733
|
+
const hooks = new Set(hooksUsed);
|
|
734
|
+
const needsDeps = hooks.has("useEffect") || hooks.has("useMemo") || hooks.has("useCallback");
|
|
735
|
+
const needsEffects = hooks.has("useEffect");
|
|
736
|
+
let code = "";
|
|
737
|
+
code += "let __currentInstance = null;\n";
|
|
738
|
+
code += "function __getCurrentInstance() {\n";
|
|
739
|
+
code += " if (!__currentInstance) {\n";
|
|
740
|
+
code += ' throw new Error("Hooks can only be called inside a component render.");\n';
|
|
741
|
+
code += " }\n";
|
|
742
|
+
code += " return __currentInstance;\n";
|
|
743
|
+
code += "}\n\n";
|
|
744
|
+
if (needsDeps) {
|
|
745
|
+
code += "function __areHookDepsEqual(prevDeps, nextDeps) {\n";
|
|
746
|
+
code += " if (!Array.isArray(prevDeps) || !Array.isArray(nextDeps)) return false;\n";
|
|
747
|
+
code += " if (prevDeps.length !== nextDeps.length) return false;\n";
|
|
748
|
+
code += " for (let i = 0; i < prevDeps.length; i += 1) {\n";
|
|
749
|
+
code += " if (!Object.is(prevDeps[i], nextDeps[i])) return false;\n";
|
|
750
|
+
code += " }\n";
|
|
751
|
+
code += " return true;\n";
|
|
752
|
+
code += "}\n\n";
|
|
753
|
+
}
|
|
754
|
+
if (needsEffects) {
|
|
755
|
+
code += "function __flushEffects(instance) {\n";
|
|
756
|
+
code += " if (!instance.pendingEffects.length) return;\n";
|
|
757
|
+
code += " const effects = instance.pendingEffects.slice();\n";
|
|
758
|
+
code += " instance.pendingEffects.length = 0;\n";
|
|
759
|
+
code += " const run = () => {\n";
|
|
760
|
+
code += " for (const item of effects) {\n";
|
|
761
|
+
code += " const previous = instance.hooks[item.index];\n";
|
|
762
|
+
code += ' if (previous && typeof previous.cleanup === "function") {\n';
|
|
763
|
+
code += " previous.cleanup();\n";
|
|
764
|
+
code += " }\n";
|
|
765
|
+
code += " const cleanup = item.effect();\n";
|
|
766
|
+
code += " instance.hooks[item.index] = {\n";
|
|
767
|
+
code += " deps: item.deps,\n";
|
|
768
|
+
code += ' cleanup: typeof cleanup === "function" ? cleanup : null,\n';
|
|
769
|
+
code += " };\n";
|
|
770
|
+
code += " }\n";
|
|
771
|
+
code += " };\n";
|
|
772
|
+
code += ' if (typeof queueMicrotask === "function") {\n';
|
|
773
|
+
code += " queueMicrotask(run);\n";
|
|
774
|
+
code += " } else {\n";
|
|
775
|
+
code += " setTimeout(run, 0);\n";
|
|
776
|
+
code += " }\n";
|
|
777
|
+
code += "}\n\n";
|
|
778
|
+
}
|
|
779
|
+
code += "function __renderInstance(instance) {\n";
|
|
780
|
+
code += " instance.hookIndex = 0;\n";
|
|
781
|
+
code += " instance.pendingEffects = [];\n";
|
|
782
|
+
code += " instance.childIndex = 0;\n";
|
|
783
|
+
code += " __currentInstance = instance;\n";
|
|
784
|
+
code += " const nextRoot = instance.component(instance.props || {});\n";
|
|
785
|
+
code += " __currentInstance = null;\n";
|
|
786
|
+
code += " if (instance.root && instance.root.parentNode) {\n";
|
|
787
|
+
code += " instance.root.parentNode.replaceChild(nextRoot, instance.root);\n";
|
|
788
|
+
code += " } else {\n";
|
|
789
|
+
code += " instance.mountTarget.appendChild(nextRoot);\n";
|
|
790
|
+
code += " }\n";
|
|
791
|
+
code += " instance.root = nextRoot;\n";
|
|
792
|
+
if (needsEffects) {
|
|
793
|
+
code += " __flushEffects(instance);\n";
|
|
794
|
+
}
|
|
795
|
+
code += "}\n\n";
|
|
796
|
+
code += "function __mount(component, mountTarget, props) {\n";
|
|
797
|
+
code += " const instance = {\n";
|
|
798
|
+
code += " component,\n";
|
|
799
|
+
code += " props: props || {},\n";
|
|
800
|
+
code += " hooks: [],\n";
|
|
801
|
+
code += " hookIndex: 0,\n";
|
|
802
|
+
code += " pendingEffects: [],\n";
|
|
803
|
+
code += " children: [],\n";
|
|
804
|
+
code += " childIndex: 0,\n";
|
|
805
|
+
code += " root: null,\n";
|
|
806
|
+
code += " mountTarget: mountTarget || document.body,\n";
|
|
807
|
+
code += " };\n";
|
|
808
|
+
code += " __renderInstance(instance);\n";
|
|
809
|
+
code += " return instance;\n";
|
|
810
|
+
code += "}\n\n";
|
|
811
|
+
code += "function __renderComponent(component, props) {\n";
|
|
812
|
+
code += " const parent = __getCurrentInstance();\n";
|
|
813
|
+
code += " const index = parent.childIndex++;\n";
|
|
814
|
+
code += " let instance = parent.children[index];\n";
|
|
815
|
+
code += " if (!instance || instance.component !== component) {\n";
|
|
816
|
+
code += " instance = {\n";
|
|
817
|
+
code += " component,\n";
|
|
818
|
+
code += " props: {},\n";
|
|
819
|
+
code += " hooks: [],\n";
|
|
820
|
+
code += " hookIndex: 0,\n";
|
|
821
|
+
code += " pendingEffects: [],\n";
|
|
822
|
+
code += " children: [],\n";
|
|
823
|
+
code += " childIndex: 0,\n";
|
|
824
|
+
code += " root: null,\n";
|
|
825
|
+
code += " mountTarget: null,\n";
|
|
826
|
+
code += " };\n";
|
|
827
|
+
code += " parent.children[index] = instance;\n";
|
|
828
|
+
code += " }\n";
|
|
829
|
+
code += " instance.props = props || {};\n";
|
|
830
|
+
code += " instance.hookIndex = 0;\n";
|
|
831
|
+
code += " instance.pendingEffects = [];\n";
|
|
832
|
+
code += " instance.childIndex = 0;\n";
|
|
833
|
+
code += " const prev = __currentInstance;\n";
|
|
834
|
+
code += " __currentInstance = instance;\n";
|
|
835
|
+
code += " const nextRoot = component(instance.props);\n";
|
|
836
|
+
code += " __currentInstance = prev;\n";
|
|
837
|
+
code += " instance.root = nextRoot;\n";
|
|
838
|
+
if (needsEffects) {
|
|
839
|
+
code += " __flushEffects(instance);\n";
|
|
840
|
+
}
|
|
841
|
+
code += " return nextRoot;\n";
|
|
842
|
+
code += "}\n\n";
|
|
843
|
+
code += "function __teardownInstance(instance) {\n";
|
|
844
|
+
code += " if (!instance) return;\n";
|
|
845
|
+
code += " if (instance.children) {\n";
|
|
846
|
+
code += " for (const child of instance.children) {\n";
|
|
847
|
+
code += " __teardownInstance(child);\n";
|
|
848
|
+
code += " }\n";
|
|
849
|
+
code += " }\n";
|
|
850
|
+
code += " if (instance.hooks) {\n";
|
|
851
|
+
code += " for (const hook of instance.hooks) {\n";
|
|
852
|
+
code += ' if (hook && typeof hook.cleanup === "function") {\n';
|
|
853
|
+
code += " hook.cleanup();\n";
|
|
854
|
+
code += " }\n";
|
|
855
|
+
code += " }\n";
|
|
856
|
+
code += " }\n";
|
|
857
|
+
code += "}\n\n";
|
|
858
|
+
if (hooks.has("useState")) {
|
|
859
|
+
code += "function useState(initialValue) {\n";
|
|
860
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
861
|
+
code += " const index = instance.hookIndex++;\n";
|
|
862
|
+
code += " if (!Object.prototype.hasOwnProperty.call(instance.hooks, index)) {\n";
|
|
863
|
+
code += ' instance.hooks[index] = typeof initialValue === "function" ? initialValue() : initialValue;\n';
|
|
864
|
+
code += " }\n";
|
|
865
|
+
code += " const setState = (value) => {\n";
|
|
866
|
+
code += ' const nextValue = typeof value === "function" ? value(instance.hooks[index]) : value;\n';
|
|
867
|
+
code += " if (!Object.is(nextValue, instance.hooks[index])) {\n";
|
|
868
|
+
code += " instance.hooks[index] = nextValue;\n";
|
|
869
|
+
code += " __renderInstance(instance);\n";
|
|
870
|
+
code += " }\n";
|
|
871
|
+
code += " };\n";
|
|
872
|
+
code += " return [instance.hooks[index], setState];\n";
|
|
873
|
+
code += "}\n\n";
|
|
874
|
+
}
|
|
875
|
+
if (hooks.has("useReducer")) {
|
|
876
|
+
code += "function useReducer(reducer, initialArg, init) {\n";
|
|
877
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
878
|
+
code += " const index = instance.hookIndex++;\n";
|
|
879
|
+
code += " if (!Object.prototype.hasOwnProperty.call(instance.hooks, index)) {\n";
|
|
880
|
+
code += " instance.hooks[index] = init ? init(initialArg) : initialArg;\n";
|
|
881
|
+
code += " }\n";
|
|
882
|
+
code += " const dispatch = (action) => {\n";
|
|
883
|
+
code += " const nextValue = reducer(instance.hooks[index], action);\n";
|
|
884
|
+
code += " if (!Object.is(nextValue, instance.hooks[index])) {\n";
|
|
885
|
+
code += " instance.hooks[index] = nextValue;\n";
|
|
886
|
+
code += " __renderInstance(instance);\n";
|
|
887
|
+
code += " }\n";
|
|
888
|
+
code += " };\n";
|
|
889
|
+
code += " return [instance.hooks[index], dispatch];\n";
|
|
890
|
+
code += "}\n\n";
|
|
891
|
+
}
|
|
892
|
+
if (hooks.has("useRef")) {
|
|
893
|
+
code += "function useRef(initialValue) {\n";
|
|
894
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
895
|
+
code += " const index = instance.hookIndex++;\n";
|
|
896
|
+
code += " if (!Object.prototype.hasOwnProperty.call(instance.hooks, index)) {\n";
|
|
897
|
+
code += " instance.hooks[index] = { current: initialValue };\n";
|
|
898
|
+
code += " }\n";
|
|
899
|
+
code += " return instance.hooks[index];\n";
|
|
900
|
+
code += "}\n\n";
|
|
901
|
+
}
|
|
902
|
+
if (hooks.has("useMemo")) {
|
|
903
|
+
code += "function useMemo(factory, deps) {\n";
|
|
904
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
905
|
+
code += " const index = instance.hookIndex++;\n";
|
|
906
|
+
code += " const prev = instance.hooks[index];\n";
|
|
907
|
+
code += " if (prev && deps && __areHookDepsEqual(prev.deps, deps)) {\n";
|
|
908
|
+
code += " return prev.value;\n";
|
|
909
|
+
code += " }\n";
|
|
910
|
+
code += " const value = factory();\n";
|
|
911
|
+
code += " instance.hooks[index] = { value, deps };\n";
|
|
912
|
+
code += " return value;\n";
|
|
913
|
+
code += "}\n\n";
|
|
914
|
+
}
|
|
915
|
+
if (hooks.has("useCallback")) {
|
|
916
|
+
code += "function useCallback(callback, deps) {\n";
|
|
917
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
918
|
+
code += " const index = instance.hookIndex++;\n";
|
|
919
|
+
code += " const prev = instance.hooks[index];\n";
|
|
920
|
+
code += " if (prev && deps && __areHookDepsEqual(prev.deps, deps)) {\n";
|
|
921
|
+
code += " return prev.callback;\n";
|
|
922
|
+
code += " }\n";
|
|
923
|
+
code += " instance.hooks[index] = { callback, deps };\n";
|
|
924
|
+
code += " return callback;\n";
|
|
925
|
+
code += "}\n\n";
|
|
926
|
+
}
|
|
927
|
+
if (hooks.has("useEffect")) {
|
|
928
|
+
code += "function useEffect(effect, deps) {\n";
|
|
929
|
+
code += " const instance = __getCurrentInstance();\n";
|
|
930
|
+
code += " const index = instance.hookIndex++;\n";
|
|
931
|
+
code += " const prev = instance.hooks[index];\n";
|
|
932
|
+
code += " const hasChanged = !prev || !deps || !__areHookDepsEqual(prev.deps, deps);\n";
|
|
933
|
+
code += " if (hasChanged) {\n";
|
|
934
|
+
code += " instance.pendingEffects.push({ index, effect, deps });\n";
|
|
935
|
+
code += " }\n";
|
|
936
|
+
code += " if (!prev) {\n";
|
|
937
|
+
code += " instance.hooks[index] = { deps, cleanup: null };\n";
|
|
938
|
+
code += " }\n";
|
|
939
|
+
code += "}\n\n";
|
|
940
|
+
}
|
|
941
|
+
if (hooks.has("useContext")) {
|
|
942
|
+
code += "function useContext(context) {\n";
|
|
943
|
+
code += " if (!context) return undefined;\n";
|
|
944
|
+
code += ' if (Object.prototype.hasOwnProperty.call(context, "value")) {\n';
|
|
945
|
+
code += " return context.value;\n";
|
|
946
|
+
code += " }\n";
|
|
947
|
+
code += " return context.current;\n";
|
|
948
|
+
code += "}\n\n";
|
|
949
|
+
}
|
|
950
|
+
return code;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/core/generator/web-component.js
|
|
954
|
+
function emitWebComponent({
|
|
955
|
+
className,
|
|
956
|
+
tagName,
|
|
957
|
+
rootComponent,
|
|
958
|
+
hooksUsed,
|
|
959
|
+
cssLiteral,
|
|
960
|
+
styleIdLiteral
|
|
961
|
+
}) {
|
|
962
|
+
const hasCss = Boolean(cssLiteral);
|
|
963
|
+
let code = "";
|
|
964
|
+
code += `class ${className} extends HTMLElement {
|
|
965
|
+
`;
|
|
966
|
+
code += " constructor() {\n";
|
|
967
|
+
code += " super();\n";
|
|
968
|
+
code += " this.__instance = null;\n";
|
|
969
|
+
code += " this.__observer = null;\n";
|
|
970
|
+
code += " this.__lightChildren = [];\n";
|
|
971
|
+
code += " this.__styleInjected = false;\n";
|
|
972
|
+
code += ' this.__shadow = this.attachShadow({ mode: "open" });\n';
|
|
973
|
+
code += " }\n\n";
|
|
974
|
+
code += " connectedCallback() {\n";
|
|
975
|
+
code += " if (!this.__lightChildren.length) {\n";
|
|
976
|
+
code += " this.__lightChildren = Array.from(this.childNodes);\n";
|
|
977
|
+
code += " }\n";
|
|
978
|
+
code += " this.__updateProps();\n";
|
|
979
|
+
code += ' if (!this.__observer && typeof MutationObserver !== "undefined") {\n';
|
|
980
|
+
code += " this.__observer = new MutationObserver(() => this.__updateProps());\n";
|
|
981
|
+
code += " this.__observer.observe(this, { attributes: true });\n";
|
|
982
|
+
code += " }\n";
|
|
983
|
+
if (hasCss) {
|
|
984
|
+
code += " if (!this.__styleInjected) {\n";
|
|
985
|
+
code += ` __applyShadowStyles(${cssLiteral}, ${styleIdLiteral}, this.__shadow, this);
|
|
986
|
+
`;
|
|
987
|
+
code += " this.__styleInjected = true;\n";
|
|
988
|
+
code += " }\n";
|
|
989
|
+
}
|
|
990
|
+
code += " }\n\n";
|
|
991
|
+
code += " __updateProps() {\n";
|
|
992
|
+
code += " const props = __getElementProps(this, this.__lightChildren);\n";
|
|
993
|
+
code += " if (!this.__instance) {\n";
|
|
994
|
+
code += ` this.__instance = __mount(${rootComponent}, this.__shadow, props);
|
|
995
|
+
`;
|
|
996
|
+
code += " } else {\n";
|
|
997
|
+
code += " this.__instance.props = props;\n";
|
|
998
|
+
code += " __renderInstance(this.__instance);\n";
|
|
999
|
+
code += " }\n";
|
|
1000
|
+
code += " }\n\n";
|
|
1001
|
+
code += " disconnectedCallback() {\n";
|
|
1002
|
+
code += " if (this.__observer) {\n";
|
|
1003
|
+
code += " this.__observer.disconnect();\n";
|
|
1004
|
+
code += " this.__observer = null;\n";
|
|
1005
|
+
code += " }\n";
|
|
1006
|
+
if (hooksUsed.length > 0) {
|
|
1007
|
+
code += ' if (typeof __teardownInstance === "function" && this.__instance) {\n';
|
|
1008
|
+
code += " __teardownInstance(this.__instance);\n";
|
|
1009
|
+
code += " }\n";
|
|
1010
|
+
}
|
|
1011
|
+
code += " }\n";
|
|
1012
|
+
code += "}\n\n";
|
|
1013
|
+
code += `if (typeof customElements !== "undefined" && !customElements.get("${tagName}")) {
|
|
1014
|
+
`;
|
|
1015
|
+
code += ` customElements.define("${tagName}", ${className});
|
|
1016
|
+
`;
|
|
1017
|
+
code += "}\n\n";
|
|
1018
|
+
return code;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// src/core/generator/index.js
|
|
1022
|
+
function generateCode(components, fileName, options = {}) {
|
|
1023
|
+
const css = options.css ?? "";
|
|
1024
|
+
const styleId = options.styleId ?? null;
|
|
1025
|
+
const hooksUsed = Array.isArray(options.hooksUsed) ? options.hooksUsed : [];
|
|
1026
|
+
const webComponent = Boolean(options.webComponent);
|
|
1027
|
+
const webComponentTag = options.webComponentTag ?? null;
|
|
1028
|
+
let code = `// Generated from ${fileName}
|
|
1029
|
+
`;
|
|
1030
|
+
code += "// Vanilla JS output (no React, no imports)\n\n";
|
|
1031
|
+
if (css) {
|
|
1032
|
+
const cssLiteral = toTemplateLiteral(css);
|
|
1033
|
+
const styleIdLiteral = styleId ? `"${styleId}"` : "null";
|
|
1034
|
+
code += "function injectStyles(cssText, styleId) {\n";
|
|
1035
|
+
code += " if (!cssText) return;\n";
|
|
1036
|
+
code += " const target = arguments.length > 2 && arguments[2] ? arguments[2] : document.head;\n";
|
|
1037
|
+
code += " if (styleId && target.querySelector && target.querySelector('#' + styleId)) return;\n";
|
|
1038
|
+
code += ' const style = document.createElement("style");\n';
|
|
1039
|
+
code += " if (styleId) style.id = styleId;\n";
|
|
1040
|
+
code += " style.textContent = cssText;\n";
|
|
1041
|
+
code += " target.appendChild(style);\n";
|
|
1042
|
+
code += "}\n\n";
|
|
1043
|
+
if (!webComponent) {
|
|
1044
|
+
code += `injectStyles(${cssLiteral}, ${styleIdLiteral}, document.head);
|
|
1045
|
+
|
|
1046
|
+
`;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
code += domRuntime;
|
|
1050
|
+
if (hooksUsed.length > 0) {
|
|
1051
|
+
code += getHookRuntime(hooksUsed);
|
|
1052
|
+
}
|
|
1053
|
+
components.forEach((comp) => {
|
|
1054
|
+
const params = comp.params ? comp.params : "";
|
|
1055
|
+
code += `// Component: ${comp.name}
|
|
1056
|
+
`;
|
|
1057
|
+
code += `function ${comp.name}(${params}) {
|
|
1058
|
+
`;
|
|
1059
|
+
code += `${comp.bodyLines.join("\n")}
|
|
1060
|
+
`;
|
|
1061
|
+
code += `}
|
|
1062
|
+
|
|
1063
|
+
`;
|
|
1064
|
+
});
|
|
1065
|
+
const rootComponent = resolveRootComponent(components, fileName);
|
|
1066
|
+
if (rootComponent) {
|
|
1067
|
+
if (webComponent) {
|
|
1068
|
+
const tagName = resolveWebComponentTag(webComponentTag, fileName, rootComponent.name);
|
|
1069
|
+
const className = toComponentClassName(tagName, rootComponent.name);
|
|
1070
|
+
code += emitWebComponent({
|
|
1071
|
+
className,
|
|
1072
|
+
tagName,
|
|
1073
|
+
rootComponent: rootComponent.name,
|
|
1074
|
+
hooksUsed,
|
|
1075
|
+
cssLiteral: css ? toTemplateLiteral(css) : null,
|
|
1076
|
+
styleIdLiteral: styleId ? `"${styleId}"` : "null"
|
|
1077
|
+
});
|
|
1078
|
+
} else if (hooksUsed.length > 0) {
|
|
1079
|
+
code += `__mount(${rootComponent.name}, document.body, __getRootProps());
|
|
1080
|
+
`;
|
|
1081
|
+
} else {
|
|
1082
|
+
code += `document.body.appendChild(${rootComponent.name}(__getRootProps()));
|
|
1083
|
+
`;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return code;
|
|
1087
|
+
}
|
|
1088
|
+
function resolveRootComponent(components, fileName) {
|
|
1089
|
+
if (!components || components.length === 0) return null;
|
|
1090
|
+
const baseName = String(fileName).split(/[\\/]/).pop() || "";
|
|
1091
|
+
const stem = baseName.replace(/\.(tsx|jsx|ts|js)$/i, "");
|
|
1092
|
+
const match = components.find((comp) => comp.name === stem);
|
|
1093
|
+
return match ?? components[0];
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/core/tailwind.js
|
|
1097
|
+
import path from "path";
|
|
1098
|
+
import { createRequire } from "module";
|
|
1099
|
+
import fs from "fs-extra";
|
|
1100
|
+
var TAILWIND_INPUT_V3 = "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n";
|
|
1101
|
+
var TAILWIND_INPUT_V4 = '@import "tailwindcss";\n';
|
|
1102
|
+
var TAILWIND_CONFIG_FILES = [
|
|
1103
|
+
"tailwind.config.js",
|
|
1104
|
+
"tailwind.config.cjs",
|
|
1105
|
+
"tailwind.config.mjs",
|
|
1106
|
+
"tailwind.config.ts"
|
|
1107
|
+
];
|
|
1108
|
+
async function buildTailwindCSS({ configPath, contentFiles, content }) {
|
|
1109
|
+
if (!configPath) return "";
|
|
1110
|
+
const resolvedConfigPath = path.resolve(configPath);
|
|
1111
|
+
const exists = await fs.pathExists(resolvedConfigPath);
|
|
1112
|
+
if (!exists) {
|
|
1113
|
+
throw new Error(`Tailwind config not found: ${resolvedConfigPath}`);
|
|
1114
|
+
}
|
|
1115
|
+
const configDir = path.dirname(resolvedConfigPath);
|
|
1116
|
+
const requireFromConfig = createRequire(path.join(configDir, "package.json"));
|
|
1117
|
+
const tailwindcss = loadModule(requireFromConfig, "tailwindcss");
|
|
1118
|
+
const tailwindPostcss = loadModule(requireFromConfig, "@tailwindcss/postcss");
|
|
1119
|
+
const postcss = loadModule(requireFromConfig, "postcss");
|
|
1120
|
+
if (!postcss || !tailwindcss && !tailwindPostcss) {
|
|
1121
|
+
throw new Error(
|
|
1122
|
+
"Tailwind build needs postcss and tailwindcss (or @tailwindcss/postcss) installed in the project."
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
const useTailwindPostcss = Boolean(tailwindPostcss);
|
|
1126
|
+
const tailwindPlugin = useTailwindPostcss ? tailwindPostcss.default ?? tailwindPostcss : tailwindcss.default ?? tailwindcss;
|
|
1127
|
+
const postcssRunner = postcss.default ?? postcss;
|
|
1128
|
+
const rawContent = [];
|
|
1129
|
+
if (Array.isArray(contentFiles) && contentFiles.length > 0) {
|
|
1130
|
+
const fromFiles = await Promise.all(
|
|
1131
|
+
contentFiles.map((file) => fs.readFile(file, "utf-8"))
|
|
1132
|
+
);
|
|
1133
|
+
rawContent.push(...fromFiles);
|
|
1134
|
+
}
|
|
1135
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
1136
|
+
rawContent.push(...content);
|
|
1137
|
+
} else if (typeof content === "string" && content) {
|
|
1138
|
+
rawContent.push(content);
|
|
1139
|
+
}
|
|
1140
|
+
if (rawContent.length === 0) return "";
|
|
1141
|
+
const tempFile = path.join(
|
|
1142
|
+
configDir,
|
|
1143
|
+
`.tailwind-raw-${Date.now()}-${Math.random().toString(36).slice(2)}.tsx`
|
|
1144
|
+
);
|
|
1145
|
+
await fs.writeFile(tempFile, rawContent.join("\n"));
|
|
1146
|
+
try {
|
|
1147
|
+
const configRelative = toRelativeCssPath(configDir, resolvedConfigPath);
|
|
1148
|
+
const sourceRelative = toRelativeCssPath(configDir, tempFile);
|
|
1149
|
+
const configForCss = toCssPath(configRelative);
|
|
1150
|
+
const sourceForCss = toCssPath(sourceRelative);
|
|
1151
|
+
const tailwindInputBody = useTailwindPostcss ? TAILWIND_INPUT_V4 : TAILWIND_INPUT_V3;
|
|
1152
|
+
const tailwindInput = `@config "${configForCss}";
|
|
1153
|
+
@source "${sourceForCss}";
|
|
1154
|
+
${tailwindInputBody}`;
|
|
1155
|
+
const pluginOptions = useTailwindPostcss ? {} : { config: resolvedConfigPath };
|
|
1156
|
+
const fromPath = path.join(configDir, ".tailwind-input.css");
|
|
1157
|
+
const result = await postcssRunner([tailwindPlugin(pluginOptions)]).process(
|
|
1158
|
+
tailwindInput,
|
|
1159
|
+
{ from: fromPath }
|
|
1160
|
+
);
|
|
1161
|
+
return result.css.trim();
|
|
1162
|
+
} finally {
|
|
1163
|
+
await fs.remove(tempFile);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function toCssPath(value) {
|
|
1167
|
+
return String(value).replace(/\\/g, "/").replace(/"/g, '\\"');
|
|
1168
|
+
}
|
|
1169
|
+
function toRelativeCssPath(baseDir, filePath) {
|
|
1170
|
+
let rel = path.relative(baseDir, filePath);
|
|
1171
|
+
if (!rel || rel.startsWith("..")) {
|
|
1172
|
+
rel = filePath;
|
|
1173
|
+
}
|
|
1174
|
+
if (!rel.startsWith(".") && !rel.startsWith("/")) {
|
|
1175
|
+
rel = `./${rel}`;
|
|
1176
|
+
}
|
|
1177
|
+
return rel;
|
|
1178
|
+
}
|
|
1179
|
+
function loadModule(requireFromConfig, moduleName) {
|
|
1180
|
+
try {
|
|
1181
|
+
return requireFromConfig(moduleName);
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
async function findTailwindConfig(startDir) {
|
|
1187
|
+
if (!startDir) return null;
|
|
1188
|
+
let currentDir = path.resolve(startDir);
|
|
1189
|
+
while (true) {
|
|
1190
|
+
for (const filename of TAILWIND_CONFIG_FILES) {
|
|
1191
|
+
const candidate = path.join(currentDir, filename);
|
|
1192
|
+
if (await fs.pathExists(candidate)) return candidate;
|
|
1193
|
+
}
|
|
1194
|
+
const parent = path.dirname(currentDir);
|
|
1195
|
+
if (parent === currentDir) break;
|
|
1196
|
+
currentDir = parent;
|
|
1197
|
+
}
|
|
1198
|
+
return null;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// src/core/tailwind-extractor.js
|
|
1202
|
+
import { parse as parse2 } from "@babel/parser";
|
|
1203
|
+
import traverseModule2 from "@babel/traverse";
|
|
1204
|
+
import * as t5 from "@babel/types";
|
|
1205
|
+
var traverse2 = traverseModule2.default ?? traverseModule2;
|
|
1206
|
+
function collectTailwindClasses(components, fileLabel = "") {
|
|
1207
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
1208
|
+
const warnings = [];
|
|
1209
|
+
if (!Array.isArray(components) || components.length === 0) {
|
|
1210
|
+
return { classNames: [], warnings };
|
|
1211
|
+
}
|
|
1212
|
+
for (const component of components) {
|
|
1213
|
+
if (!component || !Array.isArray(component.bodyLines)) continue;
|
|
1214
|
+
const componentName = component.name || "Component";
|
|
1215
|
+
const context = fileLabel ? `${fileLabel}:${componentName}` : componentName;
|
|
1216
|
+
const source = `function __tw_extract() {
|
|
1217
|
+
${component.bodyLines.join("\n")}
|
|
1218
|
+
}`;
|
|
1219
|
+
let ast;
|
|
1220
|
+
try {
|
|
1221
|
+
ast = parse2(source, {
|
|
1222
|
+
sourceType: "module",
|
|
1223
|
+
plugins: ["jsx", "typescript"]
|
|
1224
|
+
});
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
warnings.push(`${context}: unable to parse generated output for Tailwind extraction.`);
|
|
1227
|
+
continue;
|
|
1228
|
+
}
|
|
1229
|
+
traverse2(ast, {
|
|
1230
|
+
CallExpression(path2) {
|
|
1231
|
+
const { callee, arguments: args } = path2.node;
|
|
1232
|
+
if (!Array.isArray(args) || args.length < 2) return;
|
|
1233
|
+
const setAttrArgs = getSetAttributeArgs(callee, args);
|
|
1234
|
+
if (!setAttrArgs) return;
|
|
1235
|
+
const [nameNode, valueNode] = setAttrArgs;
|
|
1236
|
+
if (!t5.isStringLiteral(nameNode) || nameNode.value !== "class") return;
|
|
1237
|
+
const { tokens, dynamic } = extractClassTokens(valueNode, warnings, context);
|
|
1238
|
+
for (const token of tokens) classNames.add(token);
|
|
1239
|
+
if (dynamic) {
|
|
1240
|
+
warnings.push(`${context}: dynamic class expression detected; Tailwind output may be partial.`);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
return { classNames: Array.from(classNames), warnings };
|
|
1246
|
+
}
|
|
1247
|
+
function buildTailwindContent(classNames) {
|
|
1248
|
+
const classes = Array.isArray(classNames) ? classNames.filter(Boolean) : [];
|
|
1249
|
+
if (classes.length === 0) return "";
|
|
1250
|
+
return `<div class="${classes.join(" ")}"></div>`;
|
|
1251
|
+
}
|
|
1252
|
+
function getSetAttributeArgs(callee, args) {
|
|
1253
|
+
if (t5.isIdentifier(callee, { name: "__setAttribute" })) {
|
|
1254
|
+
if (args.length < 3) return null;
|
|
1255
|
+
return [args[1], args[2]];
|
|
1256
|
+
}
|
|
1257
|
+
if (t5.isMemberExpression(callee) && t5.isIdentifier(callee.property, { name: "setAttribute" })) {
|
|
1258
|
+
if (args.length < 2) return null;
|
|
1259
|
+
return [args[0], args[1]];
|
|
1260
|
+
}
|
|
1261
|
+
return null;
|
|
1262
|
+
}
|
|
1263
|
+
function extractClassTokens(node, warnings, context) {
|
|
1264
|
+
if (!node) return { tokens: [], dynamic: false };
|
|
1265
|
+
if (t5.isStringLiteral(node)) {
|
|
1266
|
+
return { tokens: splitClasses(node.value), dynamic: false };
|
|
1267
|
+
}
|
|
1268
|
+
if (t5.isTemplateLiteral(node)) {
|
|
1269
|
+
const tokens = [];
|
|
1270
|
+
for (const quasi of node.quasis) {
|
|
1271
|
+
const raw = quasi.value.cooked || "";
|
|
1272
|
+
tokens.push(...splitClasses(raw));
|
|
1273
|
+
}
|
|
1274
|
+
if (node.expressions.length > 0) {
|
|
1275
|
+
return { tokens, dynamic: true };
|
|
1276
|
+
}
|
|
1277
|
+
return { tokens, dynamic: false };
|
|
1278
|
+
}
|
|
1279
|
+
if (t5.isBinaryExpression(node, { operator: "+" })) {
|
|
1280
|
+
const left = extractClassTokens(node.left, warnings, context);
|
|
1281
|
+
const right = extractClassTokens(node.right, warnings, context);
|
|
1282
|
+
return {
|
|
1283
|
+
tokens: [...left.tokens, ...right.tokens],
|
|
1284
|
+
dynamic: left.dynamic || right.dynamic
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
return { tokens: [], dynamic: true };
|
|
1288
|
+
}
|
|
1289
|
+
function splitClasses(value) {
|
|
1290
|
+
return String(value).split(/\s+/).map((entry) => entry.trim()).filter(Boolean);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// src/index.js
|
|
1294
|
+
async function convert(code, options = {}) {
|
|
1295
|
+
const {
|
|
1296
|
+
fileName = "component.jsx",
|
|
1297
|
+
tailwindConfig = null,
|
|
1298
|
+
webComponent = false,
|
|
1299
|
+
webComponentTag = null,
|
|
1300
|
+
usePlaceholders = false
|
|
1301
|
+
} = options;
|
|
1302
|
+
const ast = parseFile(code);
|
|
1303
|
+
const transformed = transform(ast, { usePlaceholders });
|
|
1304
|
+
const components = transformed.components ?? transformed;
|
|
1305
|
+
const hooksUsed = transformed.hooksUsed ?? [];
|
|
1306
|
+
const { classNames, warnings } = collectTailwindClasses(components, fileName);
|
|
1307
|
+
const content = buildTailwindContent(classNames);
|
|
1308
|
+
let css = "";
|
|
1309
|
+
if (tailwindConfig && content) {
|
|
1310
|
+
css = await buildTailwindCSS({
|
|
1311
|
+
configPath: tailwindConfig,
|
|
1312
|
+
content
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
const output = generateCode(components, fileName, {
|
|
1316
|
+
css,
|
|
1317
|
+
styleId: css ? createStyleId(fileName) : null,
|
|
1318
|
+
hooksUsed,
|
|
1319
|
+
webComponent,
|
|
1320
|
+
webComponentTag
|
|
1321
|
+
});
|
|
1322
|
+
return {
|
|
1323
|
+
code: output,
|
|
1324
|
+
css,
|
|
1325
|
+
warnings
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
export {
|
|
1329
|
+
buildTailwindCSS,
|
|
1330
|
+
buildTailwindContent,
|
|
1331
|
+
collectTailwindClasses,
|
|
1332
|
+
convert,
|
|
1333
|
+
createStyleId,
|
|
1334
|
+
findTailwindConfig,
|
|
1335
|
+
generateCode,
|
|
1336
|
+
parseFile,
|
|
1337
|
+
transform
|
|
1338
|
+
};
|