@upstart.gg/vite-plugins 0.0.39 → 0.0.41
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/dist/upstart-editor-api.d.ts +79 -0
- package/dist/upstart-editor-api.d.ts.map +1 -0
- package/dist/upstart-editor-api.js +208 -0
- package/dist/upstart-editor-api.js.map +1 -0
- package/dist/vite-plugin-upstart-attrs.d.ts +3 -3
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-attrs.js +227 -25
- package/dist/vite-plugin-upstart-attrs.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.d.ts +17 -0
- package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-branding/plugin.js +41 -0
- package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -0
- package/dist/vite-plugin-upstart-branding/runtime.d.ts +10 -0
- package/dist/vite-plugin-upstart-branding/runtime.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-branding/runtime.js +118 -0
- package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -0
- package/dist/vite-plugin-upstart-branding/types.d.ts +14 -0
- package/dist/vite-plugin-upstart-branding/types.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-branding/types.js +1 -0
- package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -3
- package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.js +3 -16
- package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +25 -11
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts +5 -0
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js +16 -0
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +2 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +42 -7
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts +6 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +423 -129
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +18 -10
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-theme.d.ts +3 -3
- package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-theme.js +1 -3
- package/dist/vite-plugin-upstart-theme.js.map +1 -1
- package/package.json +12 -4
- package/src/tests/upstart-editor-api.test.ts +98 -174
- package/src/tests/vite-plugin-upstart-attrs.test.ts +408 -105
- package/src/tests/vite-plugin-upstart-branding.test.ts +90 -0
- package/src/tests/vite-plugin-upstart-editor.test.ts +1 -2
- package/src/upstart-editor-api.ts +90 -29
- package/src/vite-plugin-upstart-attrs.ts +376 -38
- package/src/vite-plugin-upstart-branding/plugin.ts +59 -0
- package/src/vite-plugin-upstart-branding/runtime.ts +128 -0
- package/src/vite-plugin-upstart-branding/types.ts +10 -0
- package/src/vite-plugin-upstart-editor/plugin.ts +4 -19
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +25 -12
- package/src/vite-plugin-upstart-editor/runtime/error-handler.ts +12 -0
- package/src/vite-plugin-upstart-editor/runtime/hover-overlay.ts +1 -1
- package/src/vite-plugin-upstart-editor/runtime/index.ts +39 -5
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +518 -141
- package/src/vite-plugin-upstart-editor/runtime/types.ts +18 -4
- package/src/vite-plugin-upstart-theme.ts +0 -3
- package/src/vite-plugin-upstart-editor/PLAN.md +0 -1391
|
@@ -66,21 +66,21 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
66
66
|
test("should track record IDs for datasource bindings", () => {
|
|
67
67
|
const code = `
|
|
68
68
|
export default function App() {
|
|
69
|
-
const user = { id: 1, name: "Alice" };
|
|
69
|
+
const user = { $id: 1, name: "Alice" };
|
|
70
70
|
return <MyComponent name={user.name} />;
|
|
71
71
|
}
|
|
72
72
|
`;
|
|
73
73
|
|
|
74
74
|
const result = transform(code);
|
|
75
75
|
|
|
76
|
-
expect(result).toContain("data-upstart-record-id-name={user
|
|
76
|
+
expect(result).toContain("data-upstart-record-id-name={user.$id}");
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
test("should handle conditional prop expressions", () => {
|
|
80
80
|
const code = `
|
|
81
81
|
export default function App() {
|
|
82
82
|
const isActive = true;
|
|
83
|
-
const user = { name: "Alice" };
|
|
83
|
+
const user = { $id: 1, name: "Alice" };
|
|
84
84
|
return <MyComponent name={isActive ? user.name : "Guest"} />;
|
|
85
85
|
}
|
|
86
86
|
`;
|
|
@@ -93,7 +93,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
93
93
|
test("should handle logical prop expressions", () => {
|
|
94
94
|
const code = `
|
|
95
95
|
export default function App() {
|
|
96
|
-
const user = { name: "Alice" };
|
|
96
|
+
const user = { $id: 1, name: "Alice" };
|
|
97
97
|
return <MyComponent name={user && user.name} />;
|
|
98
98
|
}
|
|
99
99
|
`;
|
|
@@ -105,7 +105,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
describe("Static Text Detection", () => {
|
|
108
|
-
test("should mark leaf elements with static text as editable", () => {
|
|
108
|
+
test("should mark leaf elements with static text as non-editable", () => {
|
|
109
109
|
const code = `
|
|
110
110
|
export default function App() {
|
|
111
111
|
return <div>Hello World</div>;
|
|
@@ -114,10 +114,10 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
114
114
|
|
|
115
115
|
const result = transform(code);
|
|
116
116
|
|
|
117
|
-
expect(result).toContain('data-upstart-editable-text="
|
|
117
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
test("should mark span with static text as editable", () => {
|
|
120
|
+
test("should mark span with static text as non-editable", () => {
|
|
121
121
|
const code = `
|
|
122
122
|
export default function App() {
|
|
123
123
|
return <span>Click here</span>;
|
|
@@ -126,10 +126,10 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
126
126
|
|
|
127
127
|
const result = transform(code);
|
|
128
128
|
|
|
129
|
-
expect(result).toContain('data-upstart-editable-text="
|
|
129
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
test("should mark PascalCase component with static text as editable AND track component", () => {
|
|
132
|
+
test("should mark PascalCase component with static text as non-editable AND track component", () => {
|
|
133
133
|
const code = `
|
|
134
134
|
export default function App() {
|
|
135
135
|
return <Button>Submit</Button>;
|
|
@@ -138,12 +138,12 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
138
138
|
|
|
139
139
|
const result = transform(code);
|
|
140
140
|
|
|
141
|
-
// Should have
|
|
142
|
-
expect(result).toContain('data-upstart-editable-text="
|
|
141
|
+
// Should have non-editable text AND component tracking
|
|
142
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
143
143
|
expect(result).toContain('data-upstart-component="Button"');
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
test("should NOT mark elements with nested elements", () => {
|
|
146
|
+
test("should NOT mark elements with nested elements (but mark the nested leaf)", () => {
|
|
147
147
|
const code = `
|
|
148
148
|
export default function App() {
|
|
149
149
|
return <div><span>nested</span></div>;
|
|
@@ -152,15 +152,13 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
152
152
|
|
|
153
153
|
const result = transform(code);
|
|
154
154
|
|
|
155
|
-
// The outer div should NOT be marked (
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
// Count occurrences - should only be 1 (for the span)
|
|
159
|
-
const matches = result?.match(/data-upstart-editable-text="true"/g);
|
|
155
|
+
// The outer div should NOT be marked (no direct text content)
|
|
156
|
+
// The inner span SHOULD be marked as non-editable (text leaf without Trans)
|
|
157
|
+
const matches = result?.match(/data-upstart-editable-text="false"/g);
|
|
160
158
|
expect(matches?.length).toBe(1);
|
|
161
159
|
});
|
|
162
160
|
|
|
163
|
-
test("should
|
|
161
|
+
test("should mark elements with dynamic text expressions as non-editable", () => {
|
|
164
162
|
const code = `
|
|
165
163
|
export default function App() {
|
|
166
164
|
const name = "World";
|
|
@@ -170,13 +168,12 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
170
168
|
|
|
171
169
|
const result = transform(code);
|
|
172
170
|
|
|
173
|
-
// Should
|
|
174
|
-
|
|
175
|
-
expect(result).
|
|
176
|
-
expect(result).toContain('data-upstart-hash');
|
|
171
|
+
// Should have non-editable text (Identifier expression = text-producing)
|
|
172
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
173
|
+
expect(result).toContain("data-upstart-hash");
|
|
177
174
|
});
|
|
178
175
|
|
|
179
|
-
test("should
|
|
176
|
+
test("should mark elements with mixed content as non-editable", () => {
|
|
180
177
|
const code = `
|
|
181
178
|
export default function App() {
|
|
182
179
|
return <div>Text <b>bold</b></div>;
|
|
@@ -185,10 +182,9 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
185
182
|
|
|
186
183
|
const result = transform(code);
|
|
187
184
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
expect(matches?.length).toBe(1);
|
|
185
|
+
// Both the outer div (has JSXText "Text ") and inner b (text leaf) get "false"
|
|
186
|
+
const matches = result?.match(/data-upstart-editable-text="false"/g);
|
|
187
|
+
expect(matches?.length).toBe(2);
|
|
192
188
|
});
|
|
193
189
|
|
|
194
190
|
test("should NOT mark empty elements as editable", () => {
|
|
@@ -202,8 +198,8 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
202
198
|
|
|
203
199
|
// Should NOT have editable-text (empty)
|
|
204
200
|
// But SHOULD have hash
|
|
205
|
-
expect(result).not.toContain(
|
|
206
|
-
expect(result).toContain(
|
|
201
|
+
expect(result).not.toContain("data-upstart-editable-text");
|
|
202
|
+
expect(result).toContain("data-upstart-hash");
|
|
207
203
|
});
|
|
208
204
|
|
|
209
205
|
test("should NOT mark whitespace-only elements as editable", () => {
|
|
@@ -217,8 +213,8 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
217
213
|
|
|
218
214
|
// Should NOT have editable-text (whitespace only)
|
|
219
215
|
// But SHOULD have hash
|
|
220
|
-
expect(result).not.toContain(
|
|
221
|
-
expect(result).toContain(
|
|
216
|
+
expect(result).not.toContain("data-upstart-editable-text");
|
|
217
|
+
expect(result).toContain("data-upstart-hash");
|
|
222
218
|
});
|
|
223
219
|
|
|
224
220
|
test("should handle multiple text leaf elements", () => {
|
|
@@ -235,12 +231,99 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
235
231
|
|
|
236
232
|
const result = transform(code);
|
|
237
233
|
|
|
238
|
-
// Both h1 and p should be marked
|
|
239
|
-
const matches = result?.match(/data-upstart-editable-text="
|
|
234
|
+
// Both h1 and p should be marked as non-editable
|
|
235
|
+
const matches = result?.match(/data-upstart-editable-text="false"/g);
|
|
240
236
|
expect(matches?.length).toBe(2);
|
|
241
237
|
});
|
|
242
238
|
});
|
|
243
239
|
|
|
240
|
+
describe("Non-Editable Text Detection (expressions and edge cases)", () => {
|
|
241
|
+
test("should mark element with MemberExpression as non-editable", () => {
|
|
242
|
+
const code = `
|
|
243
|
+
export default function App() {
|
|
244
|
+
return <span>{user.name}</span>;
|
|
245
|
+
}
|
|
246
|
+
`;
|
|
247
|
+
const result = transform(code);
|
|
248
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("should mark element with template literal as non-editable", () => {
|
|
252
|
+
const code = `
|
|
253
|
+
export default function App() {
|
|
254
|
+
const name = "World";
|
|
255
|
+
return <div>{\`Hello \${name}\`}</div>;
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
const result = transform(code);
|
|
259
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("should mark element with string literal expression as non-editable", () => {
|
|
263
|
+
const code = `
|
|
264
|
+
export default function App() {
|
|
265
|
+
return <div>{"Hello World"}</div>;
|
|
266
|
+
}
|
|
267
|
+
`;
|
|
268
|
+
const result = transform(code);
|
|
269
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("should NOT mark element containing only a function call expression", () => {
|
|
273
|
+
const code = `
|
|
274
|
+
export default function App() {
|
|
275
|
+
return <div>{formatDate(date)}</div>;
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
const result = transform(code);
|
|
279
|
+
expect(result).not.toContain("data-upstart-editable-text");
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("should NOT mark element containing only conditional JSX", () => {
|
|
283
|
+
const code = `
|
|
284
|
+
export default function App() {
|
|
285
|
+
return <div>{show ? <A /> : <B />}</div>;
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
const result = transform(code);
|
|
289
|
+
// Outer div should not get editable-text (ConditionalExpression is not text-producing)
|
|
290
|
+
expect(result).not.toContain('data-upstart-editable-text="false"');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("should NOT mark element containing only logical expression with JSX", () => {
|
|
294
|
+
const code = `
|
|
295
|
+
export default function App() {
|
|
296
|
+
return <div>{show && <Child />}</div>;
|
|
297
|
+
}
|
|
298
|
+
`;
|
|
299
|
+
const result = transform(code);
|
|
300
|
+
expect(result).not.toContain('data-upstart-editable-text="false"');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("should NOT mark self-closing elements", () => {
|
|
304
|
+
const code = `
|
|
305
|
+
export default function App() {
|
|
306
|
+
return <img src="photo.jpg" />;
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
309
|
+
const result = transform(code);
|
|
310
|
+
expect(result).not.toContain("data-upstart-editable-text");
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("Trans should take priority over non-editable text", () => {
|
|
314
|
+
const code = `
|
|
315
|
+
import { Trans } from "react-i18next";
|
|
316
|
+
export default function App() {
|
|
317
|
+
return <div>{prefix}<Trans i18nKey="greeting" /></div>;
|
|
318
|
+
}
|
|
319
|
+
`;
|
|
320
|
+
const result = transform(code);
|
|
321
|
+
// Trans wins: should be "true", not "false"
|
|
322
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
323
|
+
expect(result).not.toContain('data-upstart-editable-text="false"');
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
244
327
|
describe("Content Hash", () => {
|
|
245
328
|
test("should add data-upstart-hash to elements", () => {
|
|
246
329
|
const code = `
|
|
@@ -428,6 +511,27 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
428
511
|
expect(result).toContain('data-upstart-loop-array="items"');
|
|
429
512
|
});
|
|
430
513
|
|
|
514
|
+
test("should produce unique hashes for elements inside .map() with index", () => {
|
|
515
|
+
const code = `
|
|
516
|
+
export default function App() {
|
|
517
|
+
const items = ["a", "b"];
|
|
518
|
+
return (
|
|
519
|
+
<div>
|
|
520
|
+
{items.map((item, index) => (
|
|
521
|
+
<div key={index}>
|
|
522
|
+
<span>{item}</span>
|
|
523
|
+
</div>
|
|
524
|
+
))}
|
|
525
|
+
</div>
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
`;
|
|
529
|
+
const result = transform(code);
|
|
530
|
+
// Elements inside the loop should have a dynamic hash with the index
|
|
531
|
+
expect(result).toContain("data-upstart-hash={`");
|
|
532
|
+
expect(result).toContain("${index}`}");
|
|
533
|
+
});
|
|
534
|
+
|
|
431
535
|
test("should handle complex array expressions", () => {
|
|
432
536
|
const code = `
|
|
433
537
|
export default function App() {
|
|
@@ -484,7 +588,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
484
588
|
const result = transform(code);
|
|
485
589
|
|
|
486
590
|
// All elements should have hashes
|
|
487
|
-
expect(result).toContain(
|
|
591
|
+
expect(result).toContain("data-upstart-hash");
|
|
488
592
|
// Self-closing elements should not have editable-text (no text content)
|
|
489
593
|
const matches = result?.match(/data-upstart-editable-text/g);
|
|
490
594
|
expect(matches).toBeNull();
|
|
@@ -556,8 +660,8 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
556
660
|
const result = transform(code);
|
|
557
661
|
|
|
558
662
|
// Should have hash but no editable-text (comment is not editable text)
|
|
559
|
-
expect(result).toContain(
|
|
560
|
-
expect(result).not.toContain(
|
|
663
|
+
expect(result).toContain("data-upstart-hash");
|
|
664
|
+
expect(result).not.toContain("data-upstart-editable-text");
|
|
561
665
|
});
|
|
562
666
|
|
|
563
667
|
test("should handle logical expression containing JSX elements", () => {
|
|
@@ -649,7 +753,6 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
649
753
|
expect(result).toContain('data-upstart-component="ComponentA"');
|
|
650
754
|
expect(result).toContain('data-upstart-component="ComponentB"');
|
|
651
755
|
});
|
|
652
|
-
|
|
653
756
|
});
|
|
654
757
|
|
|
655
758
|
describe("Element ID Tracking", () => {
|
|
@@ -657,7 +760,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
657
760
|
clearRegistry();
|
|
658
761
|
});
|
|
659
762
|
|
|
660
|
-
test("should add data-upstart-id to text
|
|
763
|
+
test("should add data-upstart-id to text leaf elements", () => {
|
|
661
764
|
const code = `
|
|
662
765
|
export default function App() {
|
|
663
766
|
return <div>Hello World</div>;
|
|
@@ -665,8 +768,8 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
665
768
|
`;
|
|
666
769
|
const result = transform(code);
|
|
667
770
|
|
|
668
|
-
// Should have
|
|
669
|
-
expect(result).toContain('data-upstart-editable-text="
|
|
771
|
+
// Should have non-editable text AND an ID
|
|
772
|
+
expect(result).toContain('data-upstart-editable-text="false"');
|
|
670
773
|
expect(result).toMatch(/data-upstart-id="test\.tsx:\d+"/);
|
|
671
774
|
});
|
|
672
775
|
|
|
@@ -716,7 +819,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
716
819
|
const result = transform(code);
|
|
717
820
|
|
|
718
821
|
// Empty element should NOT have an ID
|
|
719
|
-
expect(result).not.toContain(
|
|
822
|
+
expect(result).not.toContain("data-upstart-id");
|
|
720
823
|
});
|
|
721
824
|
});
|
|
722
825
|
|
|
@@ -745,7 +848,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
745
848
|
`;
|
|
746
849
|
const result = transform(code);
|
|
747
850
|
|
|
748
|
-
expect(result).not.toContain(
|
|
851
|
+
expect(result).not.toContain("data-upstart-classname-id");
|
|
749
852
|
});
|
|
750
853
|
|
|
751
854
|
test("should NOT add classname-id for template literal className", () => {
|
|
@@ -756,7 +859,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
756
859
|
`;
|
|
757
860
|
const result = transform(code);
|
|
758
861
|
|
|
759
|
-
expect(result).not.toContain(
|
|
862
|
+
expect(result).not.toContain("data-upstart-classname-id");
|
|
760
863
|
});
|
|
761
864
|
|
|
762
865
|
test("should add both text-id and classname-id to same element", () => {
|
|
@@ -805,7 +908,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
805
908
|
const registry = getRegistry();
|
|
806
909
|
|
|
807
910
|
const entries = Object.values(registry);
|
|
808
|
-
const textEntry = entries.find(e => e.type === "text");
|
|
911
|
+
const textEntry = entries.find((e) => e.type === "text");
|
|
809
912
|
|
|
810
913
|
expect(textEntry).toBeDefined();
|
|
811
914
|
expect(textEntry?.originalContent).toBe("Hello World");
|
|
@@ -823,7 +926,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
823
926
|
const registry = getRegistry();
|
|
824
927
|
|
|
825
928
|
const entries = Object.values(registry);
|
|
826
|
-
const classEntry = entries.find(e => e.type === "className");
|
|
929
|
+
const classEntry = entries.find((e) => e.type === "className");
|
|
827
930
|
|
|
828
931
|
expect(classEntry).toBeDefined();
|
|
829
932
|
expect(classEntry?.originalContent).toBe("px-4 py-2");
|
|
@@ -835,7 +938,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
835
938
|
transform(code);
|
|
836
939
|
const registry = getRegistry();
|
|
837
940
|
|
|
838
|
-
const entry = Object.values(registry).find(e => e.type === "text");
|
|
941
|
+
const entry = Object.values(registry).find((e) => e.type === "text");
|
|
839
942
|
const extractedText = code.slice(entry!.startOffset, entry!.endOffset);
|
|
840
943
|
|
|
841
944
|
expect(extractedText).toBe("Hello");
|
|
@@ -847,7 +950,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
847
950
|
transform(code);
|
|
848
951
|
const registry = getRegistry();
|
|
849
952
|
|
|
850
|
-
const entry = Object.values(registry).find(e => e.type === "className");
|
|
953
|
+
const entry = Object.values(registry).find((e) => e.type === "className");
|
|
851
954
|
const extractedClass = code.slice(entry!.startOffset, entry!.endOffset);
|
|
852
955
|
|
|
853
956
|
expect(extractedClass).toBe("my-class");
|
|
@@ -859,7 +962,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
859
962
|
transform(code);
|
|
860
963
|
const registry = getRegistry();
|
|
861
964
|
|
|
862
|
-
const entry = Object.values(registry).find(e => e.type === "text");
|
|
965
|
+
const entry = Object.values(registry).find((e) => e.type === "text");
|
|
863
966
|
expect(entry?.context.parentTag).toBe("span");
|
|
864
967
|
});
|
|
865
968
|
|
|
@@ -894,8 +997,8 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
894
997
|
// Should have 2 text entries + 2 className entries = 4 total
|
|
895
998
|
expect(entries.length).toBe(4);
|
|
896
999
|
|
|
897
|
-
const textEntries = entries.filter(e => e.type === "text");
|
|
898
|
-
const classEntries = entries.filter(e => e.type === "className");
|
|
1000
|
+
const textEntries = entries.filter((e) => e.type === "text");
|
|
1001
|
+
const classEntries = entries.filter((e) => e.type === "className");
|
|
899
1002
|
|
|
900
1003
|
expect(textEntries.length).toBe(2);
|
|
901
1004
|
expect(classEntries.length).toBe(2);
|
|
@@ -956,7 +1059,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
956
1059
|
});
|
|
957
1060
|
|
|
958
1061
|
describe("i18n <Trans> Component Detection", () => {
|
|
959
|
-
test("should
|
|
1062
|
+
test("should promote <Trans> i18n attributes to parent element with default namespace", () => {
|
|
960
1063
|
const code = `
|
|
961
1064
|
import { Trans } from "react-i18next";
|
|
962
1065
|
export default function App() {
|
|
@@ -964,11 +1067,12 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
964
1067
|
}
|
|
965
1068
|
`;
|
|
966
1069
|
const result = transform(code);
|
|
967
|
-
|
|
1070
|
+
// i18n attributes should be on the parent <h3>, not on <Trans>
|
|
1071
|
+
expect(result).toContain('data-upstart-i18n="translation:features.title"');
|
|
968
1072
|
expect(result).toContain('data-upstart-editable-text="true"');
|
|
969
1073
|
});
|
|
970
1074
|
|
|
971
|
-
test("should
|
|
1075
|
+
test("should promote <Trans> with custom namespace to parent", () => {
|
|
972
1076
|
const code = `
|
|
973
1077
|
import { Trans } from "react-i18next";
|
|
974
1078
|
export default function App() {
|
|
@@ -976,11 +1080,11 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
976
1080
|
}
|
|
977
1081
|
`;
|
|
978
1082
|
const result = transform(code);
|
|
979
|
-
expect(result).toContain('data-i18n
|
|
1083
|
+
expect(result).toContain('data-upstart-i18n="dashboard:welcome.message"');
|
|
980
1084
|
expect(result).toContain('data-upstart-editable-text="true"');
|
|
981
1085
|
});
|
|
982
1086
|
|
|
983
|
-
test("should
|
|
1087
|
+
test("should NOT add i18n attributes to standalone <Trans> (no parent element)", () => {
|
|
984
1088
|
const code = `
|
|
985
1089
|
import { Trans } from "react-i18next";
|
|
986
1090
|
export default function App() {
|
|
@@ -988,11 +1092,13 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
988
1092
|
}
|
|
989
1093
|
`;
|
|
990
1094
|
const result = transform(code);
|
|
991
|
-
|
|
992
|
-
|
|
1095
|
+
// No parent element to promote to, so no modifications
|
|
1096
|
+
// transform returns null when nothing was modified
|
|
1097
|
+
expect(result === null || !result.includes("data-upstart-i18n")).toBe(true);
|
|
1098
|
+
expect(result === null || !result.includes("data-upstart-editable-text")).toBe(true);
|
|
993
1099
|
});
|
|
994
1100
|
|
|
995
|
-
test("should
|
|
1101
|
+
test("should promote multiple <Trans> components to their respective parents", () => {
|
|
996
1102
|
const code = `
|
|
997
1103
|
import { Trans } from "react-i18next";
|
|
998
1104
|
export default function App() {
|
|
@@ -1005,13 +1111,13 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1005
1111
|
}
|
|
1006
1112
|
`;
|
|
1007
1113
|
const result = transform(code);
|
|
1008
|
-
expect(result).toContain('data-i18n
|
|
1009
|
-
expect(result).toContain('data-i18n
|
|
1114
|
+
expect(result).toContain('data-upstart-i18n="translation:title"');
|
|
1115
|
+
expect(result).toContain('data-upstart-i18n="translation:description"');
|
|
1010
1116
|
const editableMatches = result?.match(/data-upstart-editable-text="true"/g);
|
|
1011
1117
|
expect(editableMatches?.length).toBe(2);
|
|
1012
1118
|
});
|
|
1013
1119
|
|
|
1014
|
-
test("should
|
|
1120
|
+
test("should promote <Trans> with values prop to parent", () => {
|
|
1015
1121
|
const code = `
|
|
1016
1122
|
import { Trans } from "react-i18next";
|
|
1017
1123
|
export default function App() {
|
|
@@ -1020,10 +1126,10 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1020
1126
|
}
|
|
1021
1127
|
`;
|
|
1022
1128
|
const result = transform(code);
|
|
1023
|
-
expect(result).toContain('data-i18n
|
|
1129
|
+
expect(result).toContain('data-upstart-i18n="translation:greeting"');
|
|
1024
1130
|
});
|
|
1025
1131
|
|
|
1026
|
-
test("should
|
|
1132
|
+
test("should promote <Trans> with components prop to parent", () => {
|
|
1027
1133
|
const code = `
|
|
1028
1134
|
import { Trans } from "react-i18next";
|
|
1029
1135
|
export default function App() {
|
|
@@ -1038,21 +1144,21 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1038
1144
|
}
|
|
1039
1145
|
`;
|
|
1040
1146
|
const result = transform(code);
|
|
1041
|
-
expect(result).toContain('data-i18n
|
|
1147
|
+
expect(result).toContain('data-upstart-i18n="translation:terms"');
|
|
1042
1148
|
});
|
|
1043
1149
|
|
|
1044
1150
|
test("should NOT detect <Trans> without i18nKey prop", () => {
|
|
1045
1151
|
const code = `
|
|
1046
1152
|
import { Trans } from "react-i18next";
|
|
1047
1153
|
export default function App() {
|
|
1048
|
-
return <Trans>Static text</Trans>;
|
|
1154
|
+
return <p><Trans>Static text</Trans></p>;
|
|
1049
1155
|
}
|
|
1050
1156
|
`;
|
|
1051
1157
|
const result = transform(code);
|
|
1052
|
-
expect(result).not.toContain(
|
|
1158
|
+
expect(result).not.toContain("data-upstart-i18n");
|
|
1053
1159
|
});
|
|
1054
1160
|
|
|
1055
|
-
test("should
|
|
1161
|
+
test("should NOT add hash or any attributes to <Trans> elements", () => {
|
|
1056
1162
|
const code = `
|
|
1057
1163
|
import { Trans } from "react-i18next";
|
|
1058
1164
|
export default function App() {
|
|
@@ -1060,11 +1166,15 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1060
1166
|
}
|
|
1061
1167
|
`;
|
|
1062
1168
|
const result = transform(code);
|
|
1063
|
-
|
|
1064
|
-
|
|
1169
|
+
// Only 1 hash (for h1), <Trans> should have no injected attributes
|
|
1170
|
+
const hashMatches = result?.match(/data-upstart-hash="[a-f0-9]+"/g);
|
|
1171
|
+
expect(hashMatches?.length).toBe(1);
|
|
1172
|
+
expect(result).toContain('data-upstart-i18n="translation:title"');
|
|
1173
|
+
// No component tracking for Trans
|
|
1174
|
+
expect(result).not.toContain('data-upstart-component="Trans"');
|
|
1065
1175
|
});
|
|
1066
1176
|
|
|
1067
|
-
test("should
|
|
1177
|
+
test("should promote i18n attrs to PascalCase component parent", () => {
|
|
1068
1178
|
const code = `
|
|
1069
1179
|
import { Trans } from "react-i18next";
|
|
1070
1180
|
export default function App() {
|
|
@@ -1072,7 +1182,7 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1072
1182
|
}
|
|
1073
1183
|
`;
|
|
1074
1184
|
const result = transform(code);
|
|
1075
|
-
expect(result).toContain('data-i18n
|
|
1185
|
+
expect(result).toContain('data-upstart-i18n="ui:submit"');
|
|
1076
1186
|
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1077
1187
|
expect(result).toContain('data-upstart-component="Button"');
|
|
1078
1188
|
});
|
|
@@ -1094,11 +1204,60 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1094
1204
|
}
|
|
1095
1205
|
`;
|
|
1096
1206
|
const result = transform(code);
|
|
1097
|
-
|
|
1207
|
+
// i18n attrs promoted to <span>, loop context on <Card>
|
|
1208
|
+
expect(result).toContain('data-upstart-i18n="translation:item.label"');
|
|
1098
1209
|
expect(result).toContain('data-upstart-loop-item="item"');
|
|
1099
1210
|
});
|
|
1100
1211
|
|
|
1101
|
-
test("should
|
|
1212
|
+
test("should detect <Trans> with dynamic expression i18nKey", () => {
|
|
1213
|
+
const code = `
|
|
1214
|
+
import { Trans } from "react-i18next";
|
|
1215
|
+
export default function App() {
|
|
1216
|
+
const labelKey = "features.title";
|
|
1217
|
+
return <h3><Trans i18nKey={labelKey} /></h3>;
|
|
1218
|
+
}
|
|
1219
|
+
`;
|
|
1220
|
+
const result = transform(code);
|
|
1221
|
+
expect(result).toContain('data-upstart-i18n="translation:features.title"');
|
|
1222
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
test("should detect <Trans> with dynamic expression i18nKey inside .map() loop", () => {
|
|
1226
|
+
const code = `
|
|
1227
|
+
import { Trans } from "react-i18next";
|
|
1228
|
+
export default function App() {
|
|
1229
|
+
const stats = [{ labelKey: "stat.users" }];
|
|
1230
|
+
return (
|
|
1231
|
+
<div>
|
|
1232
|
+
{stats.map((stat) => (
|
|
1233
|
+
<Card key={stat.labelKey}>
|
|
1234
|
+
<span><Trans i18nKey={stat.labelKey} /></span>
|
|
1235
|
+
</Card>
|
|
1236
|
+
))}
|
|
1237
|
+
</div>
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
`;
|
|
1241
|
+
const result = transform(code);
|
|
1242
|
+
expect(result).toContain("data-upstart-i18n={`translation:${stat.labelKey}`}");
|
|
1243
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1244
|
+
expect(result).toContain('data-upstart-loop-item="stat"');
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
test("should detect <Trans> with dynamic ns prop", () => {
|
|
1248
|
+
const code = `
|
|
1249
|
+
import { Trans } from "react-i18next";
|
|
1250
|
+
export default function App() {
|
|
1251
|
+
const ns = "dashboard";
|
|
1252
|
+
return <p><Trans i18nKey="welcome" ns={ns} /></p>;
|
|
1253
|
+
}
|
|
1254
|
+
`;
|
|
1255
|
+
const result = transform(code);
|
|
1256
|
+
expect(result).toContain('data-upstart-i18n="dashboard:welcome"');
|
|
1257
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
test("should promote i18n attrs to parent even with mixed content", () => {
|
|
1102
1261
|
const code = `
|
|
1103
1262
|
import { Trans } from "react-i18next";
|
|
1104
1263
|
export default function App() {
|
|
@@ -1106,84 +1265,228 @@ describe("upstart-editor-vite-plugin", () => {
|
|
|
1106
1265
|
}
|
|
1107
1266
|
`;
|
|
1108
1267
|
const result = transform(code);
|
|
1109
|
-
// The div should
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
expect(
|
|
1268
|
+
// The parent div should have the i18n key (promoted from Trans child)
|
|
1269
|
+
const matches = result?.match(/data-upstart-i18n="translation:key"/g);
|
|
1270
|
+
expect(matches?.length).toBe(1);
|
|
1271
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
test("should promote <Trans> from inside && expression to parent", () => {
|
|
1275
|
+
const code = `
|
|
1276
|
+
import { Trans } from "react-i18next";
|
|
1277
|
+
export default function App() {
|
|
1278
|
+
return <p>{showWelcome && <Trans i18nKey="welcome" />}</p>;
|
|
1279
|
+
}
|
|
1280
|
+
`;
|
|
1281
|
+
const result = transform(code);
|
|
1282
|
+
expect(result).toContain('data-upstart-i18n="translation:welcome"');
|
|
1283
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
test("should promote <Trans> from inside ternary expression to parent", () => {
|
|
1287
|
+
const code = `
|
|
1288
|
+
import { Trans } from "react-i18next";
|
|
1289
|
+
export default function App() {
|
|
1290
|
+
return <div>{isLoggedIn ? <Trans i18nKey="welcome" ns="app" /> : <Trans i18nKey="login" ns="app" />}</div>;
|
|
1291
|
+
}
|
|
1292
|
+
`;
|
|
1293
|
+
const result = transform(code);
|
|
1294
|
+
// Both keys should be comma-separated on the parent
|
|
1295
|
+
expect(result).toContain('data-upstart-i18n="app:welcome,app:login"');
|
|
1296
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
test("should preserve parent hash unchanged when promoting Trans attrs", () => {
|
|
1300
|
+
const code = `
|
|
1301
|
+
import { Trans } from "react-i18next";
|
|
1302
|
+
export default function App() {
|
|
1303
|
+
return <h1><Trans i18nKey="title" /></h1>;
|
|
1304
|
+
}
|
|
1305
|
+
`;
|
|
1306
|
+
const result1 = transform(code);
|
|
1307
|
+
const result2 = transform(code);
|
|
1308
|
+
const hash1 = result1?.match(/data-upstart-hash="([a-f0-9]+)"/)?.[1];
|
|
1309
|
+
const hash2 = result2?.match(/data-upstart-hash="([a-f0-9]+)"/)?.[1];
|
|
1310
|
+
expect(hash1).toBe(hash2);
|
|
1311
|
+
expect(hash1).toBeTruthy();
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
test("should skip <Trans> without i18nKey even in child scan", () => {
|
|
1315
|
+
const code = `
|
|
1316
|
+
import { Trans } from "react-i18next";
|
|
1317
|
+
export default function App() {
|
|
1318
|
+
return <p><Trans>Static text</Trans></p>;
|
|
1319
|
+
}
|
|
1320
|
+
`;
|
|
1321
|
+
const result = transform(code);
|
|
1322
|
+
expect(result).not.toContain("data-upstart-i18n");
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
test("should handle <Trans> inside nested logical expressions", () => {
|
|
1326
|
+
const code = `
|
|
1327
|
+
import { Trans } from "react-i18next";
|
|
1328
|
+
export default function App() {
|
|
1329
|
+
return <p>{a && b && <Trans i18nKey="nested" />}</p>;
|
|
1330
|
+
}
|
|
1331
|
+
`;
|
|
1332
|
+
const result = transform(code);
|
|
1333
|
+
expect(result).toContain('data-upstart-i18n="translation:nested"');
|
|
1113
1334
|
});
|
|
1114
1335
|
});
|
|
1115
1336
|
|
|
1116
|
-
describe("
|
|
1117
|
-
test("should
|
|
1337
|
+
describe("useTranslation t() Function Detection", () => {
|
|
1338
|
+
test("should detect t() with default namespace", () => {
|
|
1118
1339
|
const code = `
|
|
1119
1340
|
import { useTranslation } from "react-i18next";
|
|
1120
1341
|
export default function App() {
|
|
1121
1342
|
const { t } = useTranslation();
|
|
1122
|
-
return <
|
|
1343
|
+
return <h3>{t("features.title")}</h3>;
|
|
1123
1344
|
}
|
|
1124
1345
|
`;
|
|
1125
|
-
|
|
1346
|
+
const result = transform(code);
|
|
1347
|
+
expect(result).toContain('data-upstart-i18n="translation:features.title"');
|
|
1348
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1126
1349
|
});
|
|
1127
1350
|
|
|
1128
|
-
test("should
|
|
1351
|
+
test("should detect t() with custom namespace", () => {
|
|
1129
1352
|
const code = `
|
|
1130
1353
|
import { useTranslation } from "react-i18next";
|
|
1131
1354
|
export default function App() {
|
|
1132
|
-
const { t
|
|
1133
|
-
return <
|
|
1355
|
+
const { t } = useTranslation("dashboard");
|
|
1356
|
+
return <p>{t("welcome.message")}</p>;
|
|
1134
1357
|
}
|
|
1135
1358
|
`;
|
|
1136
|
-
|
|
1359
|
+
const result = transform(code);
|
|
1360
|
+
expect(result).toContain('data-upstart-i18n="dashboard:welcome.message"');
|
|
1137
1361
|
});
|
|
1138
1362
|
|
|
1139
|
-
test("should
|
|
1363
|
+
test("should detect t() with array namespace (uses first)", () => {
|
|
1140
1364
|
const code = `
|
|
1141
1365
|
import { useTranslation } from "react-i18next";
|
|
1142
1366
|
export default function App() {
|
|
1143
|
-
const { t
|
|
1144
|
-
return <
|
|
1367
|
+
const { t } = useTranslation(["dashboard", "common"]);
|
|
1368
|
+
return <p>{t("welcome")}</p>;
|
|
1145
1369
|
}
|
|
1146
1370
|
`;
|
|
1147
|
-
|
|
1371
|
+
const result = transform(code);
|
|
1372
|
+
expect(result).toContain('data-upstart-i18n="dashboard:welcome"');
|
|
1148
1373
|
});
|
|
1149
1374
|
|
|
1150
|
-
test("should
|
|
1375
|
+
test("should detect aliased t function", () => {
|
|
1151
1376
|
const code = `
|
|
1152
1377
|
import { useTranslation } from "react-i18next";
|
|
1153
1378
|
export default function App() {
|
|
1154
|
-
const {
|
|
1155
|
-
return (
|
|
1156
|
-
<button onClick={() => i18n.changeLanguage("fr")}>
|
|
1157
|
-
Switch to French
|
|
1158
|
-
</button>
|
|
1159
|
-
);
|
|
1379
|
+
const { t: translate } = useTranslation("ui");
|
|
1380
|
+
return <button>{translate("submit")}</button>;
|
|
1160
1381
|
}
|
|
1161
1382
|
`;
|
|
1162
|
-
|
|
1383
|
+
const result = transform(code);
|
|
1384
|
+
expect(result).toContain('data-upstart-i18n="ui:submit"');
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
test("should detect t() in conditional expression", () => {
|
|
1388
|
+
const code = `
|
|
1389
|
+
import { useTranslation } from "react-i18next";
|
|
1390
|
+
export default function App({ isActive }) {
|
|
1391
|
+
const { t } = useTranslation();
|
|
1392
|
+
return <span>{isActive ? t("active") : t("inactive")}</span>;
|
|
1393
|
+
}
|
|
1394
|
+
`;
|
|
1395
|
+
const result = transform(code);
|
|
1396
|
+
expect(result).toContain('data-upstart-i18n="translation:active,translation:inactive"');
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
test("should detect t() in logical expression", () => {
|
|
1400
|
+
const code = `
|
|
1401
|
+
import { useTranslation } from "react-i18next";
|
|
1402
|
+
export default function App({ showWelcome }) {
|
|
1403
|
+
const { t } = useTranslation();
|
|
1404
|
+
return <p>{showWelcome && t("welcome")}</p>;
|
|
1405
|
+
}
|
|
1406
|
+
`;
|
|
1407
|
+
const result = transform(code);
|
|
1408
|
+
expect(result).toContain('data-upstart-i18n="translation:welcome"');
|
|
1163
1409
|
});
|
|
1164
1410
|
|
|
1165
|
-
test("should
|
|
1411
|
+
test("should handle dynamic key with template expression", () => {
|
|
1412
|
+
const code = `
|
|
1413
|
+
import { useTranslation } from "react-i18next";
|
|
1414
|
+
export default function App({ stat }) {
|
|
1415
|
+
const { t } = useTranslation();
|
|
1416
|
+
return <span>{t(stat.labelKey)}</span>;
|
|
1417
|
+
}
|
|
1418
|
+
`;
|
|
1419
|
+
const result = transform(code);
|
|
1420
|
+
expect(result).toContain("data-upstart-i18n={`translation:${stat.labelKey}`}");
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
test("should resolve constant variable as static key", () => {
|
|
1166
1424
|
const code = `
|
|
1167
1425
|
import { useTranslation } from "react-i18next";
|
|
1168
1426
|
export default function App() {
|
|
1169
1427
|
const { t } = useTranslation();
|
|
1170
|
-
|
|
1428
|
+
const labelKey = "features.title";
|
|
1429
|
+
return <span>{t(labelKey)}</span>;
|
|
1171
1430
|
}
|
|
1172
1431
|
`;
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
);
|
|
1432
|
+
const result = transform(code);
|
|
1433
|
+
expect(result).toContain('data-upstart-i18n="translation:features.title"');
|
|
1176
1434
|
});
|
|
1177
1435
|
|
|
1178
|
-
test("should
|
|
1436
|
+
test("should combine Trans and t() keys in same parent", () => {
|
|
1179
1437
|
const code = `
|
|
1180
1438
|
import { useTranslation } from "react-i18next";
|
|
1439
|
+
import { Trans } from "react-i18next";
|
|
1181
1440
|
export default function App() {
|
|
1441
|
+
const { t } = useTranslation("app");
|
|
1442
|
+
return <div>{t("prefix")}<Trans i18nKey="suffix" ns="app" /></div>;
|
|
1443
|
+
}
|
|
1444
|
+
`;
|
|
1445
|
+
const result = transform(code);
|
|
1446
|
+
expect(result).toContain('data-upstart-i18n="app:suffix,app:prefix"');
|
|
1447
|
+
expect(result).toContain('data-upstart-editable-text="true"');
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
test("should detect t() inside .map() callback on child element", () => {
|
|
1451
|
+
const code = `
|
|
1452
|
+
import { useTranslation } from "react-i18next";
|
|
1453
|
+
export default function App({ items }) {
|
|
1182
1454
|
const { t } = useTranslation();
|
|
1183
|
-
return
|
|
1455
|
+
return (
|
|
1456
|
+
<ul>
|
|
1457
|
+
{items.map((item) => (
|
|
1458
|
+
<li key={item}>{t("item.label")}</li>
|
|
1459
|
+
))}
|
|
1460
|
+
</ul>
|
|
1461
|
+
);
|
|
1184
1462
|
}
|
|
1185
1463
|
`;
|
|
1186
|
-
|
|
1464
|
+
const result = transform(code);
|
|
1465
|
+
expect(result).toContain('data-upstart-i18n="translation:item.label"');
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
test("should not add i18n attributes when only i18n is destructured", () => {
|
|
1469
|
+
const code = `
|
|
1470
|
+
import { useTranslation } from "react-i18next";
|
|
1471
|
+
export default function App() {
|
|
1472
|
+
const { i18n } = useTranslation();
|
|
1473
|
+
return <button onClick={() => i18n.changeLanguage("fr")}>Switch</button>;
|
|
1474
|
+
}
|
|
1475
|
+
`;
|
|
1476
|
+
const result = transform(code);
|
|
1477
|
+
expect(result).not.toContain("data-upstart-i18n");
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
test("should track t when both t and i18n are destructured", () => {
|
|
1481
|
+
const code = `
|
|
1482
|
+
import { useTranslation } from "react-i18next";
|
|
1483
|
+
export default function App() {
|
|
1484
|
+
const { t, i18n } = useTranslation("app");
|
|
1485
|
+
return <div>{t("greeting")}</div>;
|
|
1486
|
+
}
|
|
1487
|
+
`;
|
|
1488
|
+
const result = transform(code);
|
|
1489
|
+
expect(result).toContain('data-upstart-i18n="app:greeting"');
|
|
1187
1490
|
});
|
|
1188
1491
|
});
|
|
1189
1492
|
});
|