@lingo.dev/_compiler 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2470 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;// src/index.ts
2
+ var _unplugin = require('unplugin');
3
+
4
+ // package.json
5
+ var package_default = {
6
+ name: "@lingo.dev/_compiler",
7
+ version: "0.1.1",
8
+ description: "Lingo.dev Compiler",
9
+ private: false,
10
+ publishConfig: {
11
+ access: "public"
12
+ },
13
+ sideEffects: false,
14
+ type: "module",
15
+ main: "build/index.cjs",
16
+ types: "build/index.d.ts",
17
+ module: "build/index.mjs",
18
+ files: [
19
+ "build"
20
+ ],
21
+ scripts: {
22
+ dev: "tsup --watch",
23
+ build: "tsc --noEmit && tsup",
24
+ clean: "rm -rf build",
25
+ test: "vitest --run",
26
+ "test:watch": "vitest -w"
27
+ },
28
+ keywords: [],
29
+ author: "",
30
+ license: "ISC",
31
+ devDependencies: {
32
+ "@types/babel__generator": "^7.6.8",
33
+ "@types/babel__traverse": "^7.20.6",
34
+ "@types/ini": "^4.1.1",
35
+ "@types/lodash": "^4.17.4",
36
+ "@types/object-hash": "^3.0.6",
37
+ "@types/react": "^18.3.18",
38
+ next: "15.2.4",
39
+ tsup: "^8.3.5",
40
+ typescript: "^5.4.5"
41
+ },
42
+ dependencies: {
43
+ "@ai-sdk/groq": "^1.2.3",
44
+ "@babel/generator": "^7.26.5",
45
+ "@babel/parser": "^7.26.7",
46
+ "@babel/traverse": "^7.26.7",
47
+ "@babel/types": "^7.26.7",
48
+ ai: "^4.2.10",
49
+ dedent: "^1.6.0",
50
+ dotenv: "^16.4.5",
51
+ "fast-xml-parser": "^5.0.8",
52
+ ini: "^5.0.0",
53
+ lodash: "^4.17.21",
54
+ "object-hash": "^3.0.0",
55
+ prettier: "^3.4.2",
56
+ unplugin: "^2.1.2",
57
+ vitest: "^2.1.4",
58
+ zod: "^3.24.1"
59
+ }
60
+ };
61
+
62
+ // src/index.ts
63
+ var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash);
64
+ var _dedent = require('dedent'); var _dedent2 = _interopRequireDefault(_dedent);
65
+
66
+ // src/_base.ts
67
+ var _generator = require('@babel/generator'); var _generator2 = _interopRequireDefault(_generator);
68
+ var _parser = require('@babel/parser'); var parser = _interopRequireWildcard(_parser);
69
+ function createCodeMutation(spec) {
70
+ return (payload) => {
71
+ const result = spec(payload);
72
+ return result;
73
+ };
74
+ }
75
+ function createPayload(input) {
76
+ const ast = parser.parse(input.code, {
77
+ sourceType: "module",
78
+ plugins: ["jsx", "typescript"]
79
+ });
80
+ return {
81
+ ...input,
82
+ ast
83
+ };
84
+ }
85
+ function createOutput(payload) {
86
+ const generationResult = _generator2.default.call(void 0, payload.ast, {}, payload.code);
87
+ return {
88
+ code: generationResult.code,
89
+ map: generationResult.map
90
+ };
91
+ }
92
+ function composeMutations(...mutations) {
93
+ return (input) => {
94
+ let result = input;
95
+ for (const mutate of mutations) {
96
+ const intermediateResult = mutate(result);
97
+ if (!intermediateResult) {
98
+ break;
99
+ } else {
100
+ result = intermediateResult;
101
+ }
102
+ }
103
+ return result;
104
+ };
105
+ }
106
+ var defaultParams = {
107
+ sourceRoot: "src",
108
+ lingoDir: "lingo",
109
+ sourceLocale: "en",
110
+ targetLocales: ["es"],
111
+ rsc: false,
112
+ useDirective: false,
113
+ debug: false,
114
+ models: {}
115
+ };
116
+
117
+ // src/utils/index.ts
118
+ var _traverse = require('@babel/traverse'); var _traverse2 = _interopRequireDefault(_traverse);
119
+ var _types = require('@babel/types'); var t2 = _interopRequireWildcard(_types); var t = _interopRequireWildcard(_types); var t4 = _interopRequireWildcard(_types); var t3 = _interopRequireWildcard(_types); var t5 = _interopRequireWildcard(_types); var t8 = _interopRequireWildcard(_types); var t6 = _interopRequireWildcard(_types); var t7 = _interopRequireWildcard(_types); var t10 = _interopRequireWildcard(_types); var t9 = _interopRequireWildcard(_types); var t11 = _interopRequireWildcard(_types); var t12 = _interopRequireWildcard(_types); var t13 = _interopRequireWildcard(_types); var t14 = _interopRequireWildcard(_types); var t15 = _interopRequireWildcard(_types); var t16 = _interopRequireWildcard(_types); var t17 = _interopRequireWildcard(_types); var t21 = _interopRequireWildcard(_types); var t18 = _interopRequireWildcard(_types); var t19 = _interopRequireWildcard(_types); var t20 = _interopRequireWildcard(_types); var t22 = _interopRequireWildcard(_types); var t23 = _interopRequireWildcard(_types);
120
+
121
+ // src/utils/jsx-attribute.ts
122
+
123
+
124
+ function getJsxAttributesMap(nodePath) {
125
+ const attributes = nodePath.node.openingElement.attributes;
126
+ return _lodash2.default.reduce(
127
+ attributes,
128
+ (result, attr) => {
129
+ if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier") {
130
+ return result;
131
+ }
132
+ const name = attr.name.name;
133
+ const value = extractAttributeValue(attr);
134
+ return { ...result, [name]: value };
135
+ },
136
+ {}
137
+ );
138
+ }
139
+ function getJsxAttributeValue(nodePath, attributeName) {
140
+ const attributes = nodePath.node.openingElement.attributes;
141
+ const attribute = _lodash2.default.find(
142
+ attributes,
143
+ (attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === attributeName
144
+ );
145
+ if (!attribute) {
146
+ return void 0;
147
+ }
148
+ return extractAttributeValue(attribute);
149
+ }
150
+ function extractAttributeValue(attribute) {
151
+ if (!attribute.value) {
152
+ return true;
153
+ }
154
+ if (attribute.value.type === "StringLiteral") {
155
+ return attribute.value.value;
156
+ }
157
+ if (attribute.value.type === "JSXExpressionContainer") {
158
+ const expression = attribute.value.expression;
159
+ if (expression.type === "BooleanLiteral") {
160
+ return expression.value;
161
+ }
162
+ if (expression.type === "NumericLiteral") {
163
+ return expression.value;
164
+ }
165
+ if (expression.type === "StringLiteral") {
166
+ return expression.value;
167
+ }
168
+ }
169
+ return null;
170
+ }
171
+
172
+ // src/utils/index.ts
173
+ function getJsxRoots(node) {
174
+ const result = [];
175
+ _traverse2.default.call(void 0, node, {
176
+ JSXElement(path6) {
177
+ result.push(path6);
178
+ path6.skip();
179
+ }
180
+ });
181
+ return result;
182
+ }
183
+ function getOrCreateImport(ast, params) {
184
+ let importedName = params.exportedName;
185
+ let existingImport = findExistingImport(
186
+ ast,
187
+ params.exportedName,
188
+ params.moduleName
189
+ );
190
+ if (existingImport) {
191
+ return { importedName: existingImport };
192
+ }
193
+ importedName = generateUniqueImportName(ast, params.exportedName);
194
+ createImportDeclaration(
195
+ ast,
196
+ importedName,
197
+ params.exportedName,
198
+ params.moduleName
199
+ );
200
+ return { importedName };
201
+ }
202
+ function findExistingImport(ast, exportedName, moduleName) {
203
+ let result = null;
204
+ _traverse2.default.call(void 0, ast, {
205
+ ImportDeclaration(path6) {
206
+ if (path6.node.source.value !== moduleName) {
207
+ return;
208
+ }
209
+ for (const specifier of path6.node.specifiers) {
210
+ if (t2.isImportSpecifier(specifier) && (t2.isIdentifier(specifier.imported) && specifier.imported.name === exportedName || specifier.importKind === "value" && t2.isIdentifier(specifier.local) && specifier.local.name === exportedName)) {
211
+ result = specifier.local.name;
212
+ path6.stop();
213
+ return;
214
+ }
215
+ }
216
+ }
217
+ });
218
+ return result;
219
+ }
220
+ function generateUniqueImportName(ast, baseName) {
221
+ const usedNames = /* @__PURE__ */ new Set();
222
+ _traverse2.default.call(void 0, ast, {
223
+ Identifier(path6) {
224
+ usedNames.add(path6.node.name);
225
+ }
226
+ });
227
+ if (!usedNames.has(baseName)) {
228
+ return baseName;
229
+ }
230
+ let counter = 1;
231
+ let candidateName = `${baseName}${counter}`;
232
+ while (usedNames.has(candidateName)) {
233
+ counter++;
234
+ candidateName = `${baseName}${counter}`;
235
+ }
236
+ return candidateName;
237
+ }
238
+ function createImportDeclaration(ast, localName, exportedName, moduleName) {
239
+ _traverse2.default.call(void 0, ast, {
240
+ Program(path6) {
241
+ const importSpecifier2 = t2.importSpecifier(
242
+ t2.identifier(localName),
243
+ t2.identifier(exportedName)
244
+ );
245
+ const existingImport = path6.get("body").find(
246
+ (nodePath) => t2.isImportDeclaration(nodePath.node) && nodePath.node.source.value === moduleName
247
+ );
248
+ if (existingImport && t2.isImportDeclaration(existingImport.node)) {
249
+ existingImport.node.specifiers.push(importSpecifier2);
250
+ } else {
251
+ const importDeclaration2 = t2.importDeclaration(
252
+ [importSpecifier2],
253
+ t2.stringLiteral(moduleName)
254
+ );
255
+ const lastImportIndex = findLastImportIndex(path6);
256
+ path6.node.body.splice(lastImportIndex + 1, 0, importDeclaration2);
257
+ }
258
+ path6.stop();
259
+ }
260
+ });
261
+ }
262
+ function findLastImportIndex(programPath) {
263
+ const body = programPath.node.body;
264
+ for (let i = body.length - 1; i >= 0; i--) {
265
+ if (t2.isImportDeclaration(body[i])) {
266
+ return i;
267
+ }
268
+ }
269
+ return -1;
270
+ }
271
+ function _hasFileDirective(ast, directiveValue) {
272
+ let hasDirective = false;
273
+ _traverse2.default.call(void 0, ast, {
274
+ Directive(path6) {
275
+ if (path6.node.value.value === directiveValue) {
276
+ hasDirective = true;
277
+ path6.stop();
278
+ }
279
+ }
280
+ });
281
+ return hasDirective;
282
+ }
283
+ function hasI18nDirective(ast) {
284
+ return _hasFileDirective(ast, "use i18n");
285
+ }
286
+ function hasClientDirective(ast) {
287
+ return _hasFileDirective(ast, "use client");
288
+ }
289
+ function getModuleExecutionMode(ast, rscEnabled) {
290
+ if (rscEnabled) {
291
+ if (hasClientDirective(ast)) {
292
+ return "client";
293
+ } else {
294
+ return "server";
295
+ }
296
+ } else {
297
+ return "client";
298
+ }
299
+ }
300
+
301
+ // src/i18n-directive.ts
302
+ var i18nDirectiveMutation = createCodeMutation((payload) => {
303
+ if (!payload.params.useDirective || hasI18nDirective(payload.ast)) {
304
+ return payload;
305
+ } else {
306
+ return null;
307
+ }
308
+ });
309
+ var i18n_directive_default = i18nDirectiveMutation;
310
+
311
+ // src/jsx-provider.ts
312
+
313
+
314
+
315
+ // src/utils/jsx-element.ts
316
+
317
+ function getJsxElementName(nodePath) {
318
+ const openingElement = nodePath.node.openingElement;
319
+ if (t3.isJSXIdentifier(openingElement.name)) {
320
+ return openingElement.name.name;
321
+ }
322
+ return null;
323
+ }
324
+ function getNestedJsxElements(nodePath) {
325
+ const nestedElements = [];
326
+ nodePath.traverse({
327
+ JSXElement(path6) {
328
+ if (path6.node !== nodePath.node) {
329
+ nestedElements.push(path6.node);
330
+ }
331
+ }
332
+ });
333
+ const arrayOfElements = nestedElements.map((element, index) => {
334
+ const param = t3.identifier("children");
335
+ const clonedElement = t3.cloneNode(element);
336
+ clonedElement.children = [t3.jsxExpressionContainer(param)];
337
+ return t3.arrowFunctionExpression(
338
+ [t3.objectPattern([t3.objectProperty(param, param, false, true)])],
339
+ clonedElement
340
+ );
341
+ });
342
+ const result = t3.arrayExpression(arrayOfElements);
343
+ return result;
344
+ }
345
+
346
+ // src/_const.ts
347
+ var LCP_DICTIONARY_FILE_NAME = "dictionary.js";
348
+
349
+ // src/jsx-provider.ts
350
+ var jsxProviderMutation = createCodeMutation((payload) => {
351
+ _traverse2.default.call(void 0, payload.ast, {
352
+ JSXElement: (path6) => {
353
+ if (_optionalChain([getJsxElementName, 'call', _13 => _13(path6), 'optionalAccess', _14 => _14.toLowerCase, 'call', _15 => _15()]) === "html") {
354
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
355
+ if (mode === "client") {
356
+ return;
357
+ }
358
+ const lingoProviderImport = getOrCreateImport(payload.ast, {
359
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */,
360
+ exportedName: "LingoProvider"
361
+ });
362
+ const loadDictionaryImport = getOrCreateImport(payload.ast, {
363
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */,
364
+ exportedName: "loadDictionary"
365
+ });
366
+ const loadDictionaryArrow = t4.arrowFunctionExpression(
367
+ [t4.identifier("locale")],
368
+ t4.callExpression(t4.identifier(loadDictionaryImport.importedName), [
369
+ t4.identifier("locale")
370
+ ])
371
+ );
372
+ const providerProps = [
373
+ t4.jsxAttribute(
374
+ t4.jsxIdentifier("loadDictionary"),
375
+ t4.jsxExpressionContainer(loadDictionaryArrow)
376
+ )
377
+ ];
378
+ const provider = t4.jsxElement(
379
+ t4.jsxOpeningElement(
380
+ t4.jsxIdentifier(lingoProviderImport.importedName),
381
+ providerProps,
382
+ false
383
+ ),
384
+ t4.jsxClosingElement(
385
+ t4.jsxIdentifier(lingoProviderImport.importedName)
386
+ ),
387
+ [path6.node],
388
+ false
389
+ );
390
+ path6.replaceWith(provider);
391
+ path6.skip();
392
+ }
393
+ }
394
+ });
395
+ return payload;
396
+ });
397
+ var jsx_provider_default = jsxProviderMutation;
398
+
399
+ // src/jsx-root-flag.ts
400
+
401
+ var jsxRootFlagMutation = createCodeMutation((payload) => {
402
+ const jsxRoots = getJsxRoots(payload.ast);
403
+ for (const jsxElementPath of jsxRoots) {
404
+ jsxElementPath.node.openingElement.attributes.push(
405
+ t5.jsxAttribute(t5.jsxIdentifier("data-jsx-root"), null)
406
+ );
407
+ }
408
+ return {
409
+ ...payload
410
+ };
411
+ });
412
+ var jsx_root_flag_default = jsxRootFlagMutation;
413
+
414
+ // src/jsx-scope-flag.ts
415
+
416
+
417
+ // src/utils/ast-key.ts
418
+
419
+
420
+ function getAstKey(nodePath) {
421
+ const keyChunks = [];
422
+ let current = nodePath;
423
+ while (current) {
424
+ keyChunks.push(current.key);
425
+ current = current.parentPath;
426
+ if (t6.isProgram(_optionalChain([current, 'optionalAccess', _16 => _16.node]))) {
427
+ break;
428
+ }
429
+ }
430
+ const result = keyChunks.reverse().join("/");
431
+ return result;
432
+ }
433
+
434
+ // src/utils/jsx-scope.ts
435
+
436
+
437
+ function collectJsxScopes(ast) {
438
+ const jsxScopes = [];
439
+ _traverse2.default.call(void 0, ast, {
440
+ JSXElement: (path6) => {
441
+ if (!hasJsxScopeAttribute(path6)) return;
442
+ path6.skip();
443
+ jsxScopes.push(path6);
444
+ }
445
+ });
446
+ return jsxScopes;
447
+ }
448
+ function getJsxScopes(node) {
449
+ const result = [];
450
+ _traverse2.default.call(void 0, node, {
451
+ JSXElement(path6) {
452
+ const hasNonEmptyTextSiblings = path6.getAllPrevSiblings().concat(path6.getAllNextSiblings()).some(
453
+ (sibling) => t7.isJSXText(sibling.node) && sibling.node.value.trim() !== ""
454
+ );
455
+ if (hasNonEmptyTextSiblings) {
456
+ return;
457
+ }
458
+ const hasNonEmptyTextChild = path6.get("children").some(
459
+ (child) => t7.isJSXText(child.node) && child.node.value.trim() !== ""
460
+ );
461
+ if (hasNonEmptyTextChild) {
462
+ result.push(path6);
463
+ path6.skip();
464
+ }
465
+ }
466
+ });
467
+ return result;
468
+ }
469
+ function hasJsxScopeAttribute(path6) {
470
+ return !!getJsxScopeAttribute(path6);
471
+ }
472
+ function getJsxScopeAttribute(path6) {
473
+ const attribute = path6.node.openingElement.attributes.find(
474
+ (attr) => attr.type === "JSXAttribute" && attr.name.name === "data-jsx-scope"
475
+ );
476
+ return attribute && t7.isJSXAttribute(attribute) && t7.isStringLiteral(attribute.value) ? attribute.value.value : void 0;
477
+ }
478
+
479
+ // src/jsx-scope-flag.ts
480
+ var jsxScopeFlagMutation = createCodeMutation((payload) => {
481
+ const jsxScopes = getJsxScopes(payload.ast);
482
+ for (const jsxScope of jsxScopes) {
483
+ jsxScope.node.openingElement.attributes.push(
484
+ t8.jsxAttribute(
485
+ t8.jsxIdentifier("data-jsx-scope"),
486
+ t8.stringLiteral(getAstKey(jsxScope))
487
+ )
488
+ );
489
+ }
490
+ return {
491
+ ...payload
492
+ };
493
+ });
494
+ var jsx_scope_flag_default = jsxScopeFlagMutation;
495
+
496
+ // src/jsx-attribute-flag.ts
497
+
498
+
499
+ // src/utils/jsx-attribute-scope.ts
500
+
501
+
502
+ function collectJsxAttributeScopes(node) {
503
+ const result = [];
504
+ _traverse2.default.call(void 0, node, {
505
+ JSXElement(path6) {
506
+ if (!hasJsxAttributeScopeAttribute(path6)) return;
507
+ const localizableAttributes = getJsxAttributeScopeAttribute(path6);
508
+ if (!localizableAttributes) return;
509
+ result.push([path6, localizableAttributes]);
510
+ }
511
+ });
512
+ return result;
513
+ }
514
+ function getJsxAttributeScopes(node) {
515
+ const result = [];
516
+ const LOCALIZABLE_ATTRIBUTES = [
517
+ "title",
518
+ "aria-label",
519
+ "aria-description",
520
+ "alt",
521
+ "label",
522
+ "description",
523
+ "placeholder",
524
+ "content",
525
+ "subtitle"
526
+ ];
527
+ _traverse2.default.call(void 0, node, {
528
+ JSXElement(path6) {
529
+ const openingElement = path6.node.openingElement;
530
+ const elementName = openingElement.name;
531
+ if (!t9.isJSXIdentifier(elementName) || !elementName.name) {
532
+ return;
533
+ }
534
+ const hasAttributeScope = openingElement.attributes.find(
535
+ (attr) => t9.isJSXAttribute(attr) && attr.name.name === "data-jsx-attribute-scope"
536
+ );
537
+ if (hasAttributeScope) {
538
+ return;
539
+ }
540
+ const localizableAttrs = openingElement.attributes.filter(
541
+ (attr) => {
542
+ if (!t9.isJSXAttribute(attr) || !t9.isStringLiteral(attr.value)) {
543
+ return false;
544
+ }
545
+ const name = attr.name.name;
546
+ return typeof name === "string" && LOCALIZABLE_ATTRIBUTES.includes(name);
547
+ }
548
+ ).map((attr) => attr.name.name);
549
+ if (localizableAttrs.length > 0) {
550
+ result.push([path6, localizableAttrs]);
551
+ }
552
+ }
553
+ });
554
+ return result;
555
+ }
556
+ function hasJsxAttributeScopeAttribute(path6) {
557
+ return !!getJsxAttributeScopeAttribute(path6);
558
+ }
559
+ function getJsxAttributeScopeAttribute(path6) {
560
+ const attribute = path6.node.openingElement.attributes.find(
561
+ (attr) => attr.type === "JSXAttribute" && attr.name.name === "data-jsx-attribute-scope"
562
+ );
563
+ if (!attribute || !t9.isJSXAttribute(attribute)) {
564
+ return void 0;
565
+ }
566
+ if (t9.isJSXExpressionContainer(attribute.value) && t9.isArrayExpression(attribute.value.expression)) {
567
+ const arrayExpr = attribute.value.expression;
568
+ return arrayExpr.elements.filter((el) => t9.isStringLiteral(el)).map((el) => el.value);
569
+ }
570
+ if (t9.isStringLiteral(attribute.value)) {
571
+ return [attribute.value.value];
572
+ }
573
+ return void 0;
574
+ }
575
+
576
+ // src/jsx-attribute-flag.ts
577
+ var jsxAttributeFlagMutation = createCodeMutation(
578
+ (payload) => {
579
+ const jsxScopes = getJsxAttributeScopes(payload.ast);
580
+ for (const [jsxScope, attributes] of jsxScopes) {
581
+ const scopeKey = getAstKey(jsxScope);
582
+ jsxScope.node.openingElement.attributes.push(
583
+ t10.jsxAttribute(
584
+ t10.jsxIdentifier("data-jsx-attribute-scope"),
585
+ t10.jsxExpressionContainer(
586
+ t10.arrayExpression(
587
+ attributes.map(
588
+ (attr) => t10.stringLiteral(`${attr}:${scopeKey}-${attr}`)
589
+ )
590
+ )
591
+ )
592
+ )
593
+ );
594
+ }
595
+ return {
596
+ ...payload
597
+ };
598
+ }
599
+ );
600
+ var jsx_attribute_flag_default = jsxAttributeFlagMutation;
601
+
602
+ // src/index.ts
603
+ var _path = require('path'); var path = _interopRequireWildcard(_path); var path2 = _interopRequireWildcard(_path);
604
+
605
+ // src/utils/module-params.ts
606
+ function parseParametrizedModuleId(rawId) {
607
+ const moduleUri = new URL(rawId, "module://");
608
+ return {
609
+ id: moduleUri.pathname.replace(/^\//, ""),
610
+ params: Object.fromEntries(moduleUri.searchParams.entries())
611
+ };
612
+ }
613
+
614
+ // src/lib/lcp/index.ts
615
+ var _fs = require('fs'); var fs = _interopRequireWildcard(_fs); var fs2 = _interopRequireWildcard(_fs);
616
+
617
+
618
+ var LCP_FILE_NAME = "meta.json";
619
+ var LCP = class _LCP {
620
+ constructor(filePath, data = {
621
+ version: 0.1
622
+ }) {
623
+ this.filePath = filePath;
624
+ this.data = data;
625
+ }
626
+ static getInstance(params) {
627
+ const filePath = path.resolve(
628
+ process.cwd(),
629
+ params.sourceRoot,
630
+ params.lingoDir,
631
+ LCP_FILE_NAME
632
+ );
633
+ if (fs.existsSync(filePath)) {
634
+ return new _LCP(filePath, JSON.parse(fs.readFileSync(filePath, "utf8")));
635
+ }
636
+ return new _LCP(filePath);
637
+ }
638
+ // wait until LCP file stops updating
639
+ // this ensures all files were transformed before loading / translating dictionaries
640
+ static async ready(params) {
641
+ const filePath = path.resolve(
642
+ process.cwd(),
643
+ params.sourceRoot,
644
+ params.lingoDir,
645
+ LCP_FILE_NAME
646
+ );
647
+ if (fs.existsSync(filePath)) {
648
+ const stats = fs.statSync(filePath);
649
+ if (Date.now() - stats.mtimeMs > 1500) {
650
+ return;
651
+ }
652
+ }
653
+ return new Promise((resolve3) => {
654
+ setTimeout(() => {
655
+ _LCP.ready(params).then(resolve3);
656
+ }, 750);
657
+ });
658
+ }
659
+ resetScope(fileKey, scopeKey) {
660
+ if (!_lodash2.default.isObject(
661
+ _lodash2.default.get(this.data, ["files", fileKey])
662
+ )) {
663
+ _lodash2.default.set(this.data, ["files", fileKey], {});
664
+ }
665
+ _lodash2.default.set(
666
+ this.data,
667
+ [
668
+ "files",
669
+ fileKey,
670
+ "scopes",
671
+ scopeKey
672
+ ],
673
+ {}
674
+ );
675
+ return this;
676
+ }
677
+ setScopeType(fileKey, scopeKey, type) {
678
+ return this._setScopeField(fileKey, scopeKey, "type", type);
679
+ }
680
+ setScopeContext(fileKey, scopeKey, context) {
681
+ return this._setScopeField(fileKey, scopeKey, "context", context);
682
+ }
683
+ setScopeHash(fileKey, scopeKey, hash) {
684
+ return this._setScopeField(fileKey, scopeKey, "hash", hash);
685
+ }
686
+ setScopeSkip(fileKey, scopeKey, skip) {
687
+ return this._setScopeField(fileKey, scopeKey, "skip", skip);
688
+ }
689
+ setScopeOverrides(fileKey, scopeKey, overrides) {
690
+ return this._setScopeField(fileKey, scopeKey, "overrides", overrides);
691
+ }
692
+ setScopeContent(fileKey, scopeKey, content) {
693
+ return this._setScopeField(fileKey, scopeKey, "content", content);
694
+ }
695
+ toJSON() {
696
+ const files = _lodash2.default.call(void 0, _optionalChain([this, 'access', _17 => _17.data, 'optionalAccess', _18 => _18.files])).mapValues((file, fileName) => {
697
+ return {
698
+ ...file,
699
+ scopes: _lodash2.default.call(void 0, _optionalChain([file, 'optionalAccess', _19 => _19.scopes])).toPairs().sortBy([0]).fromPairs().value()
700
+ };
701
+ }).toPairs().sortBy([0]).fromPairs().value();
702
+ return { ...this.data, files };
703
+ }
704
+ toString() {
705
+ return JSON.stringify(this.toJSON(), null, 2);
706
+ }
707
+ save() {
708
+ const hasChanges = !fs.existsSync(this.filePath) || fs.readFileSync(this.filePath, "utf8") !== this.toString();
709
+ if (hasChanges) {
710
+ const dir = this.filePath.substring(0, this.filePath.lastIndexOf("/"));
711
+ if (!fs.existsSync(dir)) {
712
+ fs.mkdirSync(dir, { recursive: true });
713
+ }
714
+ fs.writeFileSync(this.filePath, this.toString());
715
+ this._triggerLCPReload();
716
+ }
717
+ }
718
+ _triggerLCPReload() {
719
+ const dir = this.filePath.substring(0, this.filePath.lastIndexOf("/"));
720
+ const filePath = path.resolve(dir, LCP_DICTIONARY_FILE_NAME);
721
+ if (fs.existsSync(filePath)) {
722
+ const now = Date.now();
723
+ fs.utimesSync(filePath, now, now);
724
+ }
725
+ }
726
+ _setScopeField(fileKey, scopeKey, field, value) {
727
+ _lodash2.default.set(
728
+ this.data,
729
+ [
730
+ "files",
731
+ fileKey,
732
+ "scopes",
733
+ scopeKey,
734
+ field
735
+ ],
736
+ value
737
+ );
738
+ return this;
739
+ }
740
+ };
741
+
742
+ // src/lib/lcp/server.ts
743
+
744
+
745
+ // src/lib/lcp/cache.ts
746
+
747
+
748
+ var _prettier = require('prettier'); var prettier = _interopRequireWildcard(_prettier);
749
+
750
+ var LCPCache = class {
751
+ // make sure the cache file exists, otherwise imports will fail
752
+ static ensureDictionaryFile(params) {
753
+ const cachePath = this._getCachePath(params);
754
+ if (!fs2.existsSync(cachePath)) {
755
+ const dir = path2.dirname(cachePath);
756
+ if (!fs2.existsSync(dir)) {
757
+ fs2.mkdirSync(dir, { recursive: true });
758
+ }
759
+ fs2.writeFileSync(cachePath, "export default {};");
760
+ }
761
+ }
762
+ // read cache entries for given locale, validate entry hash from LCP schema
763
+ static readLocaleDictionary(locale, params) {
764
+ const cache = this._read(params);
765
+ const dictionary = this._extractLocaleDictionary(cache, locale, params.lcp);
766
+ return dictionary;
767
+ }
768
+ // write cache entries for given locale to existing cache file, use hash from LCP schema
769
+ static async writeLocaleDictionary(dictionary, params) {
770
+ const currentCache = this._read(params);
771
+ const newCache = this._mergeLocaleDictionary(
772
+ currentCache,
773
+ dictionary,
774
+ params.lcp
775
+ );
776
+ await this._write(newCache, params);
777
+ }
778
+ // merge dictionary with current cache, sort files, entries and locales to minimize diffs
779
+ static _mergeLocaleDictionary(currentCache, dictionary, lcp) {
780
+ const files = _lodash2.default.call(void 0, dictionary.files).mapValues((file, fileName) => ({
781
+ ...file,
782
+ entries: _lodash2.default.call(void 0, file.entries).mapValues((entry, entryName) => {
783
+ const cachedEntry = _nullishCoalesce(_lodash2.default.get(currentCache, ["files", fileName, "entries", entryName]), () => ( {}));
784
+ const hash = _lodash2.default.get(lcp, [
785
+ "files",
786
+ fileName,
787
+ "scopes",
788
+ entryName,
789
+ "hash"
790
+ ]);
791
+ const cachedEntryContent = cachedEntry.hash === hash ? cachedEntry.content : {};
792
+ const content = _lodash2.default.call(void 0, {
793
+ ...cachedEntryContent,
794
+ [dictionary.locale]: entry
795
+ }).toPairs().sortBy([0]).fromPairs().value();
796
+ return { content, hash };
797
+ }).toPairs().sortBy([0]).fromPairs().value()
798
+ })).toPairs().sortBy([0]).fromPairs().value();
799
+ const newCache = {
800
+ version: dictionary.version,
801
+ files
802
+ };
803
+ return newCache;
804
+ }
805
+ // extract dictionary from cache for given locale, validate entry hash from LCP schema
806
+ static _extractLocaleDictionary(cache, locale, lcp) {
807
+ const findCachedEntry = (hash) => {
808
+ const cachedEntry = _lodash2.default.call(void 0, cache.files).flatMap((file) => _lodash2.default.values(file.entries)).find((entry) => entry.hash === hash);
809
+ if (cachedEntry) {
810
+ return cachedEntry.content[locale];
811
+ }
812
+ return void 0;
813
+ };
814
+ const files = _lodash2.default.call(void 0, lcp.files).mapValues((file) => {
815
+ return {
816
+ entries: _lodash2.default.call(void 0, file.scopes).mapValues((entry) => {
817
+ return findCachedEntry(entry.hash);
818
+ }).pickBy((value) => value !== void 0).value()
819
+ };
820
+ }).pickBy((file) => !_lodash2.default.isEmpty(file.entries)).value();
821
+ const dictionary = {
822
+ version: cache.version,
823
+ locale,
824
+ files
825
+ };
826
+ return dictionary;
827
+ }
828
+ // format with prettier
829
+ static async _format(cachedContent, cachePath) {
830
+ try {
831
+ const config2 = await prettier.resolveConfig(cachePath);
832
+ const prettierOptions = {
833
+ ..._nullishCoalesce(config2, () => ( {})),
834
+ parser: _optionalChain([config2, 'optionalAccess', _20 => _20.parser]) ? config2.parser : "typescript"
835
+ };
836
+ return await prettier.format(cachedContent, prettierOptions);
837
+ } catch (error) {
838
+ }
839
+ return cachedContent;
840
+ }
841
+ // write cache to file as JSON
842
+ static async _write(dictionaryCache, params) {
843
+ const cachePath = this._getCachePath(params);
844
+ const cache = `export default ${JSON.stringify(dictionaryCache, null, 2)};`;
845
+ const formattedCache = await this._format(cache, cachePath);
846
+ fs2.writeFileSync(cachePath, formattedCache);
847
+ }
848
+ // read cache from file as JSON
849
+ static _read(params) {
850
+ const cachePath = this._getCachePath(params);
851
+ if (!fs2.existsSync(cachePath)) {
852
+ return {
853
+ version: 0.1,
854
+ files: {}
855
+ };
856
+ }
857
+ const jsObjectString = fs2.readFileSync(cachePath, "utf8");
858
+ const cache = jsObjectString.replace(/^export default/, "").replace(/;\s*$/, "");
859
+ const obj = new Function(`return (${cache})`)();
860
+ return obj;
861
+ }
862
+ // get cache file path
863
+ static _getCachePath(params) {
864
+ return path2.resolve(
865
+ process.cwd(),
866
+ params.sourceRoot,
867
+ params.lingoDir,
868
+ LCP_DICTIONARY_FILE_NAME
869
+ );
870
+ }
871
+ };
872
+
873
+ // src/lib/lcp/api/index.ts
874
+ var _groq = require('@ai-sdk/groq');
875
+ var _ai = require('ai');
876
+
877
+
878
+ // src/utils/locales.ts
879
+ function getInvalidLocales(localeModels, sourceLocale, targetLocales) {
880
+ return targetLocales.filter((targetLocale) => {
881
+ const { provider, model } = getLocaleModel(
882
+ localeModels,
883
+ sourceLocale,
884
+ targetLocale
885
+ );
886
+ return provider === void 0 || model === void 0;
887
+ });
888
+ }
889
+ function getLocaleModel(localeModels, sourceLocale, targetLocale) {
890
+ const localeKeys = [
891
+ `${sourceLocale}:${targetLocale}`,
892
+ `*:${targetLocale}`,
893
+ `${sourceLocale}:*`,
894
+ "*:*"
895
+ ];
896
+ const modelKey = localeKeys.find((key) => localeModels.hasOwnProperty(key));
897
+ if (modelKey) {
898
+ const [provider, model] = _optionalChain([localeModels, 'access', _21 => _21[modelKey], 'optionalAccess', _22 => _22.split, 'call', _23 => _23(":")]);
899
+ if (provider && model) {
900
+ return { provider, model };
901
+ }
902
+ }
903
+ return { provider: void 0, model: void 0 };
904
+ }
905
+
906
+ // src/lib/lcp/api/prompt.ts
907
+ var prompt_default = (args) => {
908
+ return getUserSystemPrompt(args) || getBuiltInSystemPrompt(args);
909
+ };
910
+ function getUserSystemPrompt(args) {
911
+ const userPrompt = _optionalChain([args, 'access', _24 => _24.prompt, 'optionalAccess', _25 => _25.trim, 'call', _26 => _26(), 'optionalAccess', _27 => _27.replace, 'call', _28 => _28("{SOURCE_LOCALE}", args.sourceLocale), 'optionalAccess', _29 => _29.replace, 'call', _30 => _30("{TARGET_LOCALE}", args.targetLocale)]);
912
+ if (userPrompt) {
913
+ console.log("\u2728 Compiler is using user-defined prompt.");
914
+ return userPrompt;
915
+ }
916
+ return void 0;
917
+ }
918
+ function getBuiltInSystemPrompt(args) {
919
+ return `
920
+ # Identity
921
+
922
+ You are an advanced AI localization engine. You do state-of-the-art localization for software products.
923
+ Your task is to localize pieces of data from one locale to another locale.
924
+ You always consider context, cultural nuances of source and target locales, and specific localization requirements.
925
+ You replicate the meaning, intent, style, tone, and purpose of the original data.
926
+
927
+ ## Setup
928
+
929
+ Source language (locale code): ${args.sourceLocale}
930
+ Target language (locale code): ${args.targetLocale}
931
+
932
+ ## Guidelines
933
+
934
+ Follow these guidelines for translation:
935
+
936
+ 1. Analyze the source text to understand its overall context and purpose
937
+ 2. Translate the meaning and intent rather than word-for-word translation
938
+ 3. Rephrase and restructure sentences to sound natural and fluent in the target language
939
+ 4. Adapt idiomatic expressions and cultural references for the target audience
940
+ 5. Maintain the style and tone of the source text
941
+ 6. You must produce valid UTF-8 encoded output
942
+ 7. YOU MUST ONLY PRODUCE VALID XML.
943
+
944
+ ## Special Instructions
945
+
946
+ Do not localize any of these technical elements:
947
+ - Variables like {variable}, {variable.key}, {data[type]}
948
+ - Expressions like <expression/>
949
+ - Functions like <function:value/>, <function:getDisplayName/>
950
+ - Elements like <element:strong>, </element:strong>, <element:LuPlus>, </element:LuPlus>, <element:LuSparkles>, </element:LuSparkles>
951
+
952
+ Remember, you are a context-aware multilingual assistant helping international companies.
953
+ Your goal is to perform state-of-the-art localization for software products and content.
954
+ `;
955
+ }
956
+
957
+ // src/lib/lcp/api/xml2obj.ts
958
+ var _fastxmlparser = require('fast-xml-parser');
959
+
960
+ var TAG_OBJECT = "object";
961
+ var TAG_ARRAY = "array";
962
+ var TAG_VALUE = "value";
963
+ function _toGenericNode(value, key) {
964
+ if (_lodash2.default.isArray(value)) {
965
+ const children = _lodash2.default.map(value, (item) => _toGenericNode(item));
966
+ return {
967
+ [TAG_ARRAY]: {
968
+ ...key ? { key } : {},
969
+ ..._groupChildren(children)
970
+ }
971
+ };
972
+ }
973
+ if (_lodash2.default.isPlainObject(value)) {
974
+ const children = _lodash2.default.map(
975
+ Object.entries(value),
976
+ ([k, v]) => _toGenericNode(v, k)
977
+ );
978
+ return {
979
+ [TAG_OBJECT]: {
980
+ ...key ? { key } : {},
981
+ ..._groupChildren(children)
982
+ }
983
+ };
984
+ }
985
+ return {
986
+ [TAG_VALUE]: {
987
+ ...key ? { key } : {},
988
+ "#text": _nullishCoalesce(value, () => ( ""))
989
+ }
990
+ };
991
+ }
992
+ function _groupChildren(nodes) {
993
+ return _lodash2.default.call(void 0, nodes).groupBy((node) => Object.keys(node)[0]).mapValues((arr) => _lodash2.default.map(arr, (n) => n[Object.keys(n)[0]])).value();
994
+ }
995
+ function _fromGenericNode(tag, data) {
996
+ if (tag === TAG_VALUE) {
997
+ if (_lodash2.default.isPlainObject(data)) {
998
+ return _lodash2.default.get(data, "#text", "");
999
+ }
1000
+ return _nullishCoalesce(data, () => ( ""));
1001
+ }
1002
+ if (tag === TAG_ARRAY) {
1003
+ const result = [];
1004
+ _lodash2.default.forEach([TAG_VALUE, TAG_OBJECT, TAG_ARRAY], (childTag) => {
1005
+ const childNodes = _lodash2.default.castArray(_lodash2.default.get(data, childTag, []));
1006
+ _lodash2.default.forEach(childNodes, (child) => {
1007
+ result.push(_fromGenericNode(childTag, child));
1008
+ });
1009
+ });
1010
+ return result;
1011
+ }
1012
+ const obj = {};
1013
+ _lodash2.default.forEach([TAG_VALUE, TAG_OBJECT, TAG_ARRAY], (childTag) => {
1014
+ const childNodes = _lodash2.default.castArray(_lodash2.default.get(data, childTag, []));
1015
+ _lodash2.default.forEach(childNodes, (child) => {
1016
+ const key = _lodash2.default.get(child, "key", "");
1017
+ obj[key] = _fromGenericNode(childTag, child);
1018
+ });
1019
+ });
1020
+ return obj;
1021
+ }
1022
+ function obj2xml(obj) {
1023
+ const rootNode = _toGenericNode(obj)[TAG_OBJECT];
1024
+ const builder = new (0, _fastxmlparser.XMLBuilder)({
1025
+ ignoreAttributes: false,
1026
+ attributeNamePrefix: "",
1027
+ format: true,
1028
+ suppressEmptyNode: true
1029
+ });
1030
+ return builder.build({ [TAG_OBJECT]: rootNode });
1031
+ }
1032
+ function xml2obj(xml) {
1033
+ const parser2 = new (0, _fastxmlparser.XMLParser)({
1034
+ ignoreAttributes: false,
1035
+ attributeNamePrefix: "",
1036
+ parseTagValue: true,
1037
+ parseAttributeValue: false,
1038
+ processEntities: true,
1039
+ isArray: (name) => [TAG_VALUE, TAG_ARRAY, TAG_OBJECT].includes(name)
1040
+ });
1041
+ const parsed = parser2.parse(xml);
1042
+ const withoutDeclaration = _lodash2.default.omit(parsed, "?xml");
1043
+ const rootTag = Object.keys(withoutDeclaration)[0];
1044
+ const rootNode = _lodash2.default.castArray(withoutDeclaration[rootTag])[0];
1045
+ return _fromGenericNode(rootTag, rootNode);
1046
+ }
1047
+
1048
+ // src/lib/lcp/api/shots.ts
1049
+ var shots_default = [
1050
+ // Shot #1
1051
+ [
1052
+ {
1053
+ version: 0.1,
1054
+ locale: "en",
1055
+ files: {
1056
+ "demo-app/my-custom-header.tsx": {
1057
+ entries: {
1058
+ "1z2x3c4v": "Dashboard",
1059
+ "5t6y7u8i": "Settings",
1060
+ "9o0p1q2r": "Logout"
1061
+ }
1062
+ },
1063
+ "demo-app/my-custom-footer.tsx": {
1064
+ entries: {
1065
+ "9k0l1m2n": "\xA9 2025 Lingo.dev. All rights reserved."
1066
+ }
1067
+ }
1068
+ }
1069
+ },
1070
+ {
1071
+ version: 0.1,
1072
+ locale: "es",
1073
+ files: {
1074
+ "demo-app/my-custom-header.tsx": {
1075
+ entries: {
1076
+ "1z2x3c4v": "Panel de control",
1077
+ "5t6y7u8i": "Configuraci\xF3n",
1078
+ "9o0p1q2r": "Cerrar sesi\xF3n"
1079
+ }
1080
+ },
1081
+ "demo-app/my-custom-footer.tsx": {
1082
+ entries: {
1083
+ "9k0l1m2n": "\xA9 2025 Lingo.dev. Todos los derechos reservados."
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ ]
1089
+ // More shots here...
1090
+ ];
1091
+
1092
+ // src/utils/rc.ts
1093
+ var _os = require('os'); var _os2 = _interopRequireDefault(_os);
1094
+
1095
+
1096
+ var _ini = require('ini'); var _ini2 = _interopRequireDefault(_ini);
1097
+ function getRc() {
1098
+ const settingsFile = ".lingodotdevrc";
1099
+ const homedir = _os2.default.homedir();
1100
+ const settingsFilePath = path.default.join(homedir, settingsFile);
1101
+ const content = fs.default.existsSync(settingsFilePath) ? fs.default.readFileSync(settingsFilePath, "utf-8") : "";
1102
+ const data = _ini2.default.parse(content);
1103
+ return data;
1104
+ }
1105
+
1106
+ // src/utils/groq.ts
1107
+
1108
+ var _dotenv = require('dotenv'); var dotenv = _interopRequireWildcard(_dotenv);
1109
+ function getGroqKey() {
1110
+ return getGroqKeyFromEnv() || getGroqKeyFromRc();
1111
+ }
1112
+ function getGroqKeyFromRc() {
1113
+ const rc = getRc();
1114
+ const result = _lodash2.default.get(rc, "llm.groqApiKey");
1115
+ return result;
1116
+ }
1117
+ function getGroqKeyFromEnv() {
1118
+ const ephemeralEnv = {
1119
+ GROQ_API_KEY: process.env.GROQ_API_KEY
1120
+ };
1121
+ dotenv.config({ processEnv: ephemeralEnv });
1122
+ return ephemeralEnv.GROQ_API_KEY;
1123
+ }
1124
+
1125
+ // src/lib/lcp/api/index.ts
1126
+
1127
+
1128
+ // src/utils/env.ts
1129
+
1130
+ function isRunningInCIOrDocker() {
1131
+ return Boolean(process.env.CI) || fs.default.existsSync("/.dockerenv");
1132
+ }
1133
+
1134
+ // src/lib/lcp/api/index.ts
1135
+ var LCPAPI = class {
1136
+ static async translate(models, sourceDictionary, sourceLocale, targetLocale) {
1137
+ const timeLabel = `LCPAPI.translate: ${targetLocale}`;
1138
+ console.time(timeLabel);
1139
+ const chunks = this._chunkDictionary(sourceDictionary);
1140
+ const translatedChunks = [];
1141
+ for (const chunk of chunks) {
1142
+ const translatedChunk = await this._translateChunk(
1143
+ models,
1144
+ chunk,
1145
+ sourceLocale,
1146
+ targetLocale
1147
+ );
1148
+ translatedChunks.push(translatedChunk);
1149
+ }
1150
+ const result = this._mergeDictionaries(translatedChunks);
1151
+ console.timeEnd(timeLabel);
1152
+ return result;
1153
+ }
1154
+ static _chunkDictionary(dictionary) {
1155
+ const MAX_ENTRIES_PER_CHUNK = 100;
1156
+ const { files, ...rest } = dictionary;
1157
+ const chunks = [];
1158
+ let currentChunk = {
1159
+ ...rest,
1160
+ files: {}
1161
+ };
1162
+ let currentEntryCount = 0;
1163
+ Object.entries(files).forEach(([fileName, file]) => {
1164
+ const entries = file.entries;
1165
+ const entryPairs = Object.entries(entries);
1166
+ let currentIndex = 0;
1167
+ while (currentIndex < entryPairs.length) {
1168
+ const remainingSpace = MAX_ENTRIES_PER_CHUNK - currentEntryCount;
1169
+ const entriesToAdd = entryPairs.slice(
1170
+ currentIndex,
1171
+ currentIndex + remainingSpace
1172
+ );
1173
+ if (entriesToAdd.length > 0) {
1174
+ currentChunk.files[fileName] = currentChunk.files[fileName] || {
1175
+ entries: {}
1176
+ };
1177
+ currentChunk.files[fileName].entries = {
1178
+ ...currentChunk.files[fileName].entries,
1179
+ ...Object.fromEntries(entriesToAdd)
1180
+ };
1181
+ currentEntryCount += entriesToAdd.length;
1182
+ }
1183
+ currentIndex += entriesToAdd.length;
1184
+ if (currentEntryCount >= MAX_ENTRIES_PER_CHUNK || currentIndex < entryPairs.length && currentEntryCount + (entryPairs.length - currentIndex) > MAX_ENTRIES_PER_CHUNK) {
1185
+ chunks.push(currentChunk);
1186
+ currentChunk = { ...rest, files: {} };
1187
+ currentEntryCount = 0;
1188
+ }
1189
+ }
1190
+ });
1191
+ if (currentEntryCount > 0) {
1192
+ chunks.push(currentChunk);
1193
+ }
1194
+ return chunks;
1195
+ }
1196
+ static _mergeDictionaries(dictionaries) {
1197
+ const fileNames = _lodash2.default.uniq(
1198
+ _lodash2.default.flatMap(dictionaries, (dict) => Object.keys(dict.files))
1199
+ );
1200
+ const files = _lodash2.default.call(void 0, fileNames).map((fileName) => {
1201
+ const entries = dictionaries.reduce((entries2, dict) => {
1202
+ const file = dict.files[fileName];
1203
+ if (file) {
1204
+ entries2 = _lodash2.default.merge(entries2, file.entries);
1205
+ }
1206
+ return entries2;
1207
+ }, {});
1208
+ return [fileName, { entries }];
1209
+ }).fromPairs().value();
1210
+ const dictionary = {
1211
+ version: dictionaries[0].version,
1212
+ locale: dictionaries[0].locale,
1213
+ files
1214
+ };
1215
+ return dictionary;
1216
+ }
1217
+ static async _translateChunk(models, sourceDictionary, sourceLocale, targetLocale) {
1218
+ try {
1219
+ return await this._translateChunkGroq(
1220
+ models,
1221
+ sourceDictionary,
1222
+ sourceLocale,
1223
+ targetLocale
1224
+ );
1225
+ } catch (e) {
1226
+ console.error(`\u26A0\uFE0F Translation for ${targetLocale} failed:`, e);
1227
+ return {
1228
+ version: 0.1,
1229
+ locale: targetLocale,
1230
+ files: {}
1231
+ };
1232
+ }
1233
+ if (isRunningInCIOrDocker()) {
1234
+ const groqFromEnv = getGroqKeyFromEnv();
1235
+ if (!groqFromEnv) {
1236
+ this._failMissingGroqKeyCi();
1237
+ }
1238
+ }
1239
+ throw new Error(
1240
+ "\u26A0\uFE0F No API key found. Please set GROQ_API_KEY environment variable. If you dont have one go to https://groq.com/"
1241
+ );
1242
+ }
1243
+ static async _translateChunkGroq(models, sourceDictionary, sourceLocale, targetLocale) {
1244
+ const groq = _groq.createGroq.call(void 0, { apiKey: getGroqKey() });
1245
+ console.log(`Created Groq client for ${targetLocale}`);
1246
+ const { provider, model } = getLocaleModel(
1247
+ models,
1248
+ sourceLocale,
1249
+ targetLocale
1250
+ );
1251
+ if (!model) {
1252
+ throw new Error(
1253
+ `\u26A0\uFE0F Locale ${targetLocale} is not configured. Add model for this locale to your config.`
1254
+ );
1255
+ }
1256
+ if (provider !== "groq") {
1257
+ throw new Error(
1258
+ `\u26A0\uFE0F Provider ${provider} is not supported. Only "groq" provider is supported at the moment.`
1259
+ );
1260
+ }
1261
+ console.log(
1262
+ `\u2728 Using model "${model}" from "${provider}" to translate from "${sourceLocale}" to "${targetLocale}"`
1263
+ );
1264
+ const groqModel = groq(model);
1265
+ console.log(`Created model ${model}`);
1266
+ const response = await _ai.generateText.call(void 0, {
1267
+ model: groqModel,
1268
+ messages: [
1269
+ {
1270
+ role: "system",
1271
+ content: prompt_default({ sourceLocale, targetLocale })
1272
+ },
1273
+ ...shots_default.flatMap((shotsTuple) => [
1274
+ {
1275
+ role: "user",
1276
+ content: obj2xml(shotsTuple[0])
1277
+ },
1278
+ {
1279
+ role: "assistant",
1280
+ content: obj2xml(shotsTuple[1])
1281
+ }
1282
+ ]),
1283
+ {
1284
+ role: "user",
1285
+ content: obj2xml(sourceDictionary)
1286
+ }
1287
+ ]
1288
+ });
1289
+ console.log("Response", response.text);
1290
+ let responseText = response.text;
1291
+ responseText = responseText.substring(
1292
+ responseText.indexOf("<"),
1293
+ responseText.lastIndexOf(">") + 1
1294
+ );
1295
+ return xml2obj(responseText);
1296
+ }
1297
+ /**
1298
+ * Show an actionable error message and exit the process when the compiler
1299
+ * is running in CI/CD without a GROQ API key.
1300
+ * The message explains why this situation is unusual and how to fix it.
1301
+ */
1302
+ static _failMissingGroqKeyCi() {
1303
+ console.log(
1304
+ _dedent2.default`
1305
+ \n
1306
+ 💡 You're using Lingo.dev Localization Compiler, and it detected unlocalized components in your app.
1307
+
1308
+ The compiler needs a GROQ API key to translate missing strings, but GROQ_API_KEY is not set in the environment.
1309
+
1310
+ This is unexpected: typically you run a full build locally, commit the generated translation files, and push them to CI/CD.
1311
+
1312
+ However, If you want CI/CD to translate the new strings, provide the key with:
1313
+ • Session-wide: export GROQ_API_KEY=<your-api-key>
1314
+ • Project-wide / CI: add GROQ_API_KEY=<your-api-key> to your pipeline environment variables
1315
+
1316
+ ⭐️ Also:
1317
+ 1. If you don't yet have a GROQ API key, get one for free at https://groq.com
1318
+ 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
1319
+ 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
1320
+
1321
+
1322
+ `
1323
+ );
1324
+ process.exit(1);
1325
+ }
1326
+ };
1327
+
1328
+ // src/lib/lcp/server.ts
1329
+ var LCPServer = (_class = class {
1330
+ static __initStatic() {this.isLoading = false}
1331
+ static async loadDictionaries(params) {
1332
+ while (this.isLoading) {
1333
+ await new Promise(function(resolve3) {
1334
+ setTimeout(resolve3, 500);
1335
+ });
1336
+ }
1337
+ this.isLoading = true;
1338
+ const targetLocales = _lodash2.default.uniq([
1339
+ ...params.targetLocales,
1340
+ params.sourceLocale
1341
+ ]);
1342
+ const dictionaries = await Promise.all(
1343
+ targetLocales.map(
1344
+ (targetLocale) => this.loadDictionaryForLocale({ ...params, targetLocale })
1345
+ )
1346
+ );
1347
+ const result = _lodash2.default.fromPairs(
1348
+ targetLocales.map((targetLocale, index) => [
1349
+ targetLocale,
1350
+ dictionaries[index]
1351
+ ])
1352
+ );
1353
+ this.isLoading = false;
1354
+ return result;
1355
+ }
1356
+ static async loadDictionaryForLocale(params) {
1357
+ const sourceDictionary = this._extractSourceDictionary(
1358
+ params.lcp,
1359
+ params.sourceLocale,
1360
+ params.targetLocale
1361
+ );
1362
+ const cacheParams = {
1363
+ lcp: params.lcp,
1364
+ sourceLocale: params.sourceLocale,
1365
+ lingoDir: params.lingoDir,
1366
+ sourceRoot: params.sourceRoot
1367
+ };
1368
+ if (this._countDictionaryEntries(sourceDictionary) === 0) {
1369
+ console.log(
1370
+ "Source dictionary is empty, returning empty dictionary for target locale"
1371
+ );
1372
+ return { ...sourceDictionary, locale: params.targetLocale };
1373
+ }
1374
+ const cache = LCPCache.readLocaleDictionary(
1375
+ params.targetLocale,
1376
+ cacheParams
1377
+ );
1378
+ const uncachedSourceDictionary = this._getDictionaryDiff(
1379
+ sourceDictionary,
1380
+ cache
1381
+ );
1382
+ let targetDictionary;
1383
+ let newTranslations;
1384
+ if (this._countDictionaryEntries(uncachedSourceDictionary) === 0) {
1385
+ targetDictionary = cache;
1386
+ } else if (params.targetLocale === params.sourceLocale) {
1387
+ console.log(
1388
+ "\u2139\uFE0F Lingo.dev returns source dictionary - source and target locales are the same"
1389
+ );
1390
+ await LCPCache.writeLocaleDictionary(sourceDictionary, cacheParams);
1391
+ return sourceDictionary;
1392
+ } else {
1393
+ newTranslations = await LCPAPI.translate(
1394
+ params.models,
1395
+ uncachedSourceDictionary,
1396
+ params.sourceLocale,
1397
+ params.targetLocale
1398
+ );
1399
+ targetDictionary = this._mergeDictionaries(newTranslations, cache);
1400
+ await LCPCache.writeLocaleDictionary(targetDictionary, cacheParams);
1401
+ }
1402
+ const targetDictionaryWithFallback = this._mergeDictionaries(
1403
+ targetDictionary,
1404
+ sourceDictionary,
1405
+ true
1406
+ );
1407
+ const result = this._addOverridesToDictionary(
1408
+ targetDictionaryWithFallback,
1409
+ params.lcp,
1410
+ params.targetLocale
1411
+ );
1412
+ if (newTranslations) {
1413
+ console.log(
1414
+ `\u2139\uFE0F Lingo.dev dictionary for ${params.targetLocale}:
1415
+ - %d entries
1416
+ - %d cached
1417
+ - %d uncached
1418
+ - %d translated
1419
+ - %d overrides`,
1420
+ this._countDictionaryEntries(result),
1421
+ this._countDictionaryEntries(cache),
1422
+ this._countDictionaryEntries(uncachedSourceDictionary),
1423
+ newTranslations ? this._countDictionaryEntries(newTranslations) : 0,
1424
+ this._countDictionaryEntries(result) - this._countDictionaryEntries(targetDictionary)
1425
+ );
1426
+ }
1427
+ return result;
1428
+ }
1429
+ static _extractSourceDictionary(lcp, sourceLocale, targetLocale) {
1430
+ const dictionary = {
1431
+ version: 0.1,
1432
+ locale: sourceLocale,
1433
+ files: {}
1434
+ };
1435
+ for (const [fileKey, fileData] of Object.entries(lcp.files || {})) {
1436
+ for (const [scopeKey, scopeData] of Object.entries(
1437
+ fileData.scopes || {}
1438
+ )) {
1439
+ if (scopeData.skip) {
1440
+ continue;
1441
+ }
1442
+ if (this._getScopeLocaleOverride(scopeData, targetLocale)) {
1443
+ continue;
1444
+ }
1445
+ _lodash2.default.set(
1446
+ dictionary,
1447
+ [
1448
+ "files",
1449
+ fileKey,
1450
+ "entries",
1451
+ scopeKey
1452
+ ],
1453
+ scopeData.content
1454
+ );
1455
+ }
1456
+ }
1457
+ return dictionary;
1458
+ }
1459
+ static _addOverridesToDictionary(dictionary, lcp, targetLocale) {
1460
+ for (const [fileKey, fileData] of Object.entries(lcp.files || {})) {
1461
+ for (const [scopeKey, scopeData] of Object.entries(
1462
+ fileData.scopes || {}
1463
+ )) {
1464
+ const override = this._getScopeLocaleOverride(scopeData, targetLocale);
1465
+ if (!override) {
1466
+ continue;
1467
+ }
1468
+ _lodash2.default.set(
1469
+ dictionary,
1470
+ [
1471
+ "files",
1472
+ fileKey,
1473
+ "entries",
1474
+ scopeKey
1475
+ ],
1476
+ override
1477
+ );
1478
+ }
1479
+ }
1480
+ return dictionary;
1481
+ }
1482
+ static _getScopeLocaleOverride(scopeData, locale) {
1483
+ return _nullishCoalesce(_lodash2.default.get(scopeData.overrides, locale), () => ( null));
1484
+ }
1485
+ static _getDictionaryDiff(sourceDictionary, targetDictionary) {
1486
+ if (this._countDictionaryEntries(targetDictionary) === 0) {
1487
+ return sourceDictionary;
1488
+ }
1489
+ const files = _lodash2.default.call(void 0, sourceDictionary.files).mapValues((file, fileName) => ({
1490
+ ...file,
1491
+ entries: _lodash2.default.call(void 0, file.entries).mapValues((entry, entryName) => {
1492
+ const targetEntry = _lodash2.default.get(targetDictionary.files, [
1493
+ fileName,
1494
+ "entries",
1495
+ entryName
1496
+ ]);
1497
+ if (targetEntry !== void 0) {
1498
+ return void 0;
1499
+ }
1500
+ return entry;
1501
+ }).pickBy((value) => value !== void 0).value()
1502
+ })).pickBy((value) => Object.keys(value.entries).length > 0).value();
1503
+ const dictionary = {
1504
+ version: sourceDictionary.version,
1505
+ locale: sourceDictionary.locale,
1506
+ files
1507
+ };
1508
+ return dictionary;
1509
+ }
1510
+ static _mergeDictionaries(sourceDictionary, targetDictionary, removeEmptyEntries = false) {
1511
+ const fileNames = _lodash2.default.uniq([
1512
+ ...Object.keys(sourceDictionary.files),
1513
+ ...Object.keys(targetDictionary.files)
1514
+ ]);
1515
+ const files = _lodash2.default.call(void 0, fileNames).map((fileName) => {
1516
+ const sourceFile = _lodash2.default.get(sourceDictionary.files, fileName);
1517
+ const targetFile = _lodash2.default.get(targetDictionary.files, fileName);
1518
+ const entries = removeEmptyEntries ? _lodash2.default.pickBy(_optionalChain([sourceFile, 'optionalAccess', _31 => _31.entries]), (value) => value.trim().length > 0) : _optionalChain([sourceFile, 'optionalAccess', _32 => _32.entries]);
1519
+ return [
1520
+ fileName,
1521
+ {
1522
+ ...targetFile,
1523
+ entries: _lodash2.default.merge(_optionalChain([targetFile, 'optionalAccess', _33 => _33.entries]), entries)
1524
+ }
1525
+ ];
1526
+ }).fromPairs().value();
1527
+ const dictionary = {
1528
+ version: sourceDictionary.version,
1529
+ locale: sourceDictionary.locale,
1530
+ files
1531
+ };
1532
+ return dictionary;
1533
+ }
1534
+ static _countDictionaryEntries(dict) {
1535
+ return Object.values(dict.files).reduce(
1536
+ (sum, file) => sum + Object.keys(file.entries).length,
1537
+ 0
1538
+ );
1539
+ }
1540
+ }, _class.__initStatic(), _class);
1541
+
1542
+ // src/utils/invokations.ts
1543
+
1544
+
1545
+ function findInvokations(ast, params) {
1546
+ const result = [];
1547
+ _traverse2.default.call(void 0, ast, {
1548
+ ImportDeclaration(path6) {
1549
+ if (path6.node.source.value !== params.moduleName) return;
1550
+ const importNames = /* @__PURE__ */ new Map();
1551
+ const specifiers = path6.node.specifiers;
1552
+ specifiers.forEach((specifier) => {
1553
+ if (t11.isImportSpecifier(specifier) && t11.isIdentifier(specifier.imported) && specifier.imported.name === params.functionName) {
1554
+ importNames.set(specifier.local.name, true);
1555
+ } else if (t11.isImportDefaultSpecifier(specifier) && params.functionName === "default") {
1556
+ importNames.set(specifier.local.name, true);
1557
+ } else if (t11.isImportNamespaceSpecifier(specifier)) {
1558
+ importNames.set(specifier.local.name, "namespace");
1559
+ }
1560
+ });
1561
+ collectCallExpressions(path6, importNames, result, params.functionName);
1562
+ }
1563
+ });
1564
+ return result;
1565
+ }
1566
+ function collectCallExpressions(path6, importNames, result, functionName) {
1567
+ const program = path6.findParent(
1568
+ (p) => p.isProgram()
1569
+ );
1570
+ if (!program) return;
1571
+ program.traverse({
1572
+ CallExpression(callPath) {
1573
+ const callee = callPath.node.callee;
1574
+ if (t11.isIdentifier(callee) && importNames.has(callee.name)) {
1575
+ result.push(callPath.node);
1576
+ } else if (t11.isMemberExpression(callee) && t11.isIdentifier(callee.object) && importNames.get(callee.object.name) === "namespace" && t11.isIdentifier(callee.property) && callee.property.name === functionName) {
1577
+ result.push(callPath.node);
1578
+ }
1579
+ }
1580
+ });
1581
+ }
1582
+
1583
+ // src/rsc-dictionary-loader.ts
1584
+
1585
+ var rscDictionaryLoaderMutation = createCodeMutation((payload) => {
1586
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
1587
+ if (mode === "client") {
1588
+ return payload;
1589
+ }
1590
+ const invokations = findInvokations(payload.ast, {
1591
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */,
1592
+ functionName: "loadDictionary"
1593
+ });
1594
+ const allLocales = Array.from(
1595
+ /* @__PURE__ */ new Set([payload.params.sourceLocale, ...payload.params.targetLocales])
1596
+ );
1597
+ for (const invokation of invokations) {
1598
+ const internalDictionaryLoader = getOrCreateImport(payload.ast, {
1599
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */,
1600
+ exportedName: "loadDictionary_internal"
1601
+ });
1602
+ if (t12.isIdentifier(invokation.callee)) {
1603
+ invokation.callee.name = internalDictionaryLoader.importedName;
1604
+ }
1605
+ const localeImportMap = t12.objectExpression(
1606
+ allLocales.map(
1607
+ (locale) => t12.objectProperty(
1608
+ t12.identifier(locale),
1609
+ t12.arrowFunctionExpression(
1610
+ [],
1611
+ t12.callExpression(t12.identifier("import"), [
1612
+ t12.stringLiteral(
1613
+ `@/${payload.params.lingoDir}/${LCP_DICTIONARY_FILE_NAME}?locale=${locale}`
1614
+ )
1615
+ ])
1616
+ )
1617
+ )
1618
+ )
1619
+ );
1620
+ invokation.arguments.push(localeImportMap);
1621
+ }
1622
+ return payload;
1623
+ });
1624
+
1625
+ // src/react-router-dictionary-loader.ts
1626
+
1627
+ var reactRouterDictionaryLoaderMutation = createCodeMutation(
1628
+ (payload) => {
1629
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
1630
+ if (mode === "server") {
1631
+ return payload;
1632
+ }
1633
+ const invokations = findInvokations(payload.ast, {
1634
+ moduleName: "lingo.dev/react/react-router" /* ReactRouter */,
1635
+ functionName: "loadDictionary"
1636
+ });
1637
+ const allLocales = Array.from(
1638
+ /* @__PURE__ */ new Set([payload.params.sourceLocale, ...payload.params.targetLocales])
1639
+ );
1640
+ for (const invokation of invokations) {
1641
+ const internalDictionaryLoader = getOrCreateImport(payload.ast, {
1642
+ moduleName: "lingo.dev/react/react-router" /* ReactRouter */,
1643
+ exportedName: "loadDictionary_internal"
1644
+ });
1645
+ if (t13.isIdentifier(invokation.callee)) {
1646
+ invokation.callee.name = internalDictionaryLoader.importedName;
1647
+ }
1648
+ const localeImportMap = t13.objectExpression(
1649
+ allLocales.map(
1650
+ (locale) => t13.objectProperty(
1651
+ t13.identifier(locale),
1652
+ t13.arrowFunctionExpression(
1653
+ [],
1654
+ t13.callExpression(t13.identifier("import"), [
1655
+ t13.stringLiteral(
1656
+ `~/${payload.params.lingoDir}/${LCP_DICTIONARY_FILE_NAME}?locale=${locale}`
1657
+ )
1658
+ ])
1659
+ )
1660
+ )
1661
+ )
1662
+ );
1663
+ invokation.arguments.push(localeImportMap);
1664
+ }
1665
+ return payload;
1666
+ }
1667
+ );
1668
+
1669
+ // src/jsx-fragment.ts
1670
+
1671
+
1672
+ function jsxFragmentMutation(payload) {
1673
+ const { ast } = payload;
1674
+ let foundFragments = false;
1675
+ let fragmentImportName = null;
1676
+ _traverse2.default.call(void 0, ast, {
1677
+ ImportDeclaration(path6) {
1678
+ if (path6.node.source.value !== "react") return;
1679
+ for (const specifier of path6.node.specifiers) {
1680
+ if (t14.isImportSpecifier(specifier) && t14.isIdentifier(specifier.imported) && specifier.imported.name === "Fragment") {
1681
+ fragmentImportName = specifier.local.name;
1682
+ path6.stop();
1683
+ }
1684
+ }
1685
+ }
1686
+ });
1687
+ _traverse2.default.call(void 0, ast, {
1688
+ JSXFragment(path6) {
1689
+ foundFragments = true;
1690
+ if (!fragmentImportName) {
1691
+ const result = getOrCreateImport(ast, {
1692
+ exportedName: "Fragment",
1693
+ moduleName: "react"
1694
+ });
1695
+ fragmentImportName = result.importedName;
1696
+ }
1697
+ const fragmentElement = t14.jsxElement(
1698
+ t14.jsxOpeningElement(t14.jsxIdentifier(fragmentImportName), [], false),
1699
+ t14.jsxClosingElement(t14.jsxIdentifier(fragmentImportName)),
1700
+ path6.node.children,
1701
+ false
1702
+ );
1703
+ path6.replaceWith(fragmentElement);
1704
+ }
1705
+ });
1706
+ return payload;
1707
+ }
1708
+
1709
+ // src/jsx-html-lang.ts
1710
+
1711
+
1712
+ var jsxHtmlLangMutation = createCodeMutation((payload) => {
1713
+ _traverse2.default.call(void 0, payload.ast, {
1714
+ JSXElement: (path6) => {
1715
+ if (_optionalChain([getJsxElementName, 'call', _34 => _34(path6), 'optionalAccess', _35 => _35.toLowerCase, 'call', _36 => _36()]) === "html") {
1716
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
1717
+ const packagePath = mode === "client" ? "lingo.dev/react/client" /* ReactClient */ : "lingo.dev/react/rsc" /* ReactRSC */;
1718
+ const lingoHtmlComponentImport = getOrCreateImport(payload.ast, {
1719
+ moduleName: packagePath,
1720
+ exportedName: "LingoHtmlComponent"
1721
+ });
1722
+ path6.node.openingElement.name = t15.jsxIdentifier(
1723
+ lingoHtmlComponentImport.importedName
1724
+ );
1725
+ if (path6.node.closingElement) {
1726
+ path6.node.closingElement.name = t15.jsxIdentifier(
1727
+ lingoHtmlComponentImport.importedName
1728
+ );
1729
+ }
1730
+ path6.skip();
1731
+ }
1732
+ }
1733
+ });
1734
+ return payload;
1735
+ });
1736
+
1737
+ // src/utils/hash.ts
1738
+ var _objecthash = require('object-hash');
1739
+ function getJsxElementHash(nodePath) {
1740
+ if (!nodePath.node) {
1741
+ return "";
1742
+ }
1743
+ const content = nodePath.node.children.map((child) => child.value).join("");
1744
+ const result = _objecthash.MD5.call(void 0, content);
1745
+ return result;
1746
+ }
1747
+ function getJsxAttributeValueHash(attributeValue) {
1748
+ if (!attributeValue) {
1749
+ return "";
1750
+ }
1751
+ const result = _objecthash.MD5.call(void 0, attributeValue);
1752
+ return result;
1753
+ }
1754
+
1755
+ // src/jsx-attribute-scopes-export.ts
1756
+
1757
+ function jsxAttributeScopesExportMutation(payload) {
1758
+ const attributeScopes = collectJsxAttributeScopes(payload.ast);
1759
+ if (_lodash2.default.isEmpty(attributeScopes)) {
1760
+ return payload;
1761
+ }
1762
+ const lcp = LCP.getInstance({
1763
+ sourceRoot: payload.params.sourceRoot,
1764
+ lingoDir: payload.params.lingoDir
1765
+ });
1766
+ for (const [scope, attributes] of attributeScopes) {
1767
+ for (const attributeDefinition of attributes) {
1768
+ const [attribute, scopeKey] = attributeDefinition.split(":");
1769
+ lcp.resetScope(payload.fileKey, scopeKey);
1770
+ const attributeValue = getJsxAttributeValue(scope, attribute);
1771
+ if (!attributeValue) {
1772
+ continue;
1773
+ }
1774
+ lcp.setScopeType(payload.fileKey, scopeKey, "attribute");
1775
+ const hash = getJsxAttributeValueHash(String(attributeValue));
1776
+ lcp.setScopeHash(payload.fileKey, scopeKey, hash);
1777
+ lcp.setScopeContext(payload.fileKey, scopeKey, "");
1778
+ lcp.setScopeSkip(payload.fileKey, scopeKey, false);
1779
+ lcp.setScopeOverrides(payload.fileKey, scopeKey, {});
1780
+ lcp.setScopeContent(payload.fileKey, scopeKey, String(attributeValue));
1781
+ }
1782
+ }
1783
+ lcp.save();
1784
+ return payload;
1785
+ }
1786
+
1787
+ // src/jsx-scopes-export.ts
1788
+
1789
+
1790
+ // src/utils/jsx-content.ts
1791
+
1792
+
1793
+ var WHITESPACE_PLACEHOLDER = "[lingo-whitespace-placeholder]";
1794
+ function extractJsxContent(nodePath, replaceWhitespacePlaceholders = true) {
1795
+ const chunks = [];
1796
+ nodePath.traverse({
1797
+ JSXElement(path6) {
1798
+ if (path6.parent === nodePath.node) {
1799
+ const content = extractJsxContent(path6, false);
1800
+ const name = getJsxElementName(path6);
1801
+ chunks.push(`<element:${name}>${content}</element:${name}>`);
1802
+ path6.skip();
1803
+ }
1804
+ },
1805
+ JSXText(path6) {
1806
+ chunks.push(path6.node.value);
1807
+ },
1808
+ JSXExpressionContainer(path6) {
1809
+ if (path6.parent !== nodePath.node) {
1810
+ return;
1811
+ }
1812
+ const expr = path6.node.expression;
1813
+ if (t16.isCallExpression(expr)) {
1814
+ let key = "";
1815
+ if (t16.isIdentifier(expr.callee)) {
1816
+ key = `${expr.callee.name}`;
1817
+ } else if (t16.isMemberExpression(expr.callee)) {
1818
+ let firstCallee = expr.callee;
1819
+ while (t16.isMemberExpression(firstCallee) && t16.isCallExpression(firstCallee.object)) {
1820
+ firstCallee = firstCallee.object.callee;
1821
+ }
1822
+ let current = firstCallee;
1823
+ const parts = [];
1824
+ while (t16.isMemberExpression(current)) {
1825
+ if (t16.isIdentifier(current.property)) {
1826
+ parts.unshift(current.property.name);
1827
+ }
1828
+ current = current.object;
1829
+ }
1830
+ if (t16.isIdentifier(current)) {
1831
+ parts.unshift(current.name);
1832
+ }
1833
+ if (t16.isMemberExpression(firstCallee) && t16.isNewExpression(firstCallee.object) && t16.isIdentifier(firstCallee.object.callee)) {
1834
+ parts.unshift(firstCallee.object.callee.name);
1835
+ }
1836
+ key = parts.join(".");
1837
+ }
1838
+ chunks.push(`<function:${key}/>`);
1839
+ } else if (t16.isIdentifier(expr)) {
1840
+ chunks.push(`{${expr.name}}`);
1841
+ } else if (t16.isMemberExpression(expr)) {
1842
+ let current = expr;
1843
+ const parts = [];
1844
+ while (t16.isMemberExpression(current)) {
1845
+ if (t16.isIdentifier(current.property)) {
1846
+ if (current.computed) {
1847
+ parts.unshift(`[${current.property.name}]`);
1848
+ } else {
1849
+ parts.unshift(current.property.name);
1850
+ }
1851
+ }
1852
+ current = current.object;
1853
+ }
1854
+ if (t16.isIdentifier(current)) {
1855
+ parts.unshift(current.name);
1856
+ chunks.push(`{${parts.join(".").replaceAll(".[", "[")}}`);
1857
+ }
1858
+ } else if (isWhitespace(path6)) {
1859
+ chunks.push(WHITESPACE_PLACEHOLDER);
1860
+ } else if (isExpression2(path6)) {
1861
+ chunks.push("<expression/>");
1862
+ }
1863
+ path6.skip();
1864
+ }
1865
+ });
1866
+ const result = chunks.join("");
1867
+ const normalized = normalizeJsxWhitespace(result);
1868
+ if (replaceWhitespacePlaceholders) {
1869
+ return normalized.replaceAll(WHITESPACE_PLACEHOLDER, " ");
1870
+ }
1871
+ return normalized;
1872
+ }
1873
+ var compilerProps = ["data-jsx-attribute-scope", "data-jsx-scope"];
1874
+ function isExpression2(nodePath) {
1875
+ const isCompilerExpression = !_lodash2.default.isArray(nodePath.container) && t16.isJSXAttribute(nodePath.container) && t16.isJSXIdentifier(nodePath.container.name) && compilerProps.includes(nodePath.container.name.name);
1876
+ return !isCompilerExpression && !t16.isJSXEmptyExpression(nodePath.node.expression);
1877
+ }
1878
+ function isWhitespace(nodePath) {
1879
+ const expr = nodePath.node.expression;
1880
+ return t16.isStringLiteral(expr) && expr.value === " ";
1881
+ }
1882
+ function normalizeJsxWhitespace(input) {
1883
+ const lines = input.split("\n");
1884
+ let result = "";
1885
+ for (let i = 0; i < lines.length; i++) {
1886
+ const line = lines[i].trim();
1887
+ if (line === "") continue;
1888
+ if (i > 0 && (line.startsWith("<element:") || line.startsWith("<function:") || line.startsWith("{") || line.startsWith("<expression/>"))) {
1889
+ result += line;
1890
+ } else {
1891
+ if (result && !result.endsWith(" ")) result += " ";
1892
+ result += line;
1893
+ }
1894
+ }
1895
+ result = result.replace(/\s+/g, " ");
1896
+ return result.trim();
1897
+ }
1898
+
1899
+ // src/jsx-scopes-export.ts
1900
+ function jsxScopesExportMutation(payload) {
1901
+ const scopes = collectJsxScopes(payload.ast);
1902
+ if (_lodash2.default.isEmpty(scopes)) {
1903
+ return payload;
1904
+ }
1905
+ const lcp = LCP.getInstance({
1906
+ sourceRoot: payload.params.sourceRoot,
1907
+ lingoDir: payload.params.lingoDir
1908
+ });
1909
+ for (const scope of scopes) {
1910
+ const scopeKey = getAstKey(scope);
1911
+ lcp.resetScope(payload.fileKey, scopeKey);
1912
+ lcp.setScopeType(payload.fileKey, scopeKey, "element");
1913
+ const hash = getJsxElementHash(scope);
1914
+ lcp.setScopeHash(payload.fileKey, scopeKey, hash);
1915
+ const context = getJsxAttributeValue(scope, "data-lingo-context");
1916
+ lcp.setScopeContext(payload.fileKey, scopeKey, String(context || ""));
1917
+ const skip = getJsxAttributeValue(scope, "data-lingo-skip");
1918
+ lcp.setScopeSkip(payload.fileKey, scopeKey, Boolean(skip || false));
1919
+ const attributesMap = getJsxAttributesMap(scope);
1920
+ const overrides = _lodash2.default.chain(attributesMap).entries().filter(
1921
+ ([attributeKey]) => attributeKey.startsWith("data-lingo-override-")
1922
+ ).map(([k, v]) => [k.split("data-lingo-override-")[1], v]).filter(([k]) => !!k).filter(([, v]) => !!v).fromPairs().value();
1923
+ lcp.setScopeOverrides(payload.fileKey, scopeKey, overrides);
1924
+ const content = extractJsxContent(scope);
1925
+ lcp.setScopeContent(payload.fileKey, scopeKey, content);
1926
+ }
1927
+ lcp.save();
1928
+ return payload;
1929
+ }
1930
+
1931
+ // src/jsx-attribute-scope-inject.ts
1932
+
1933
+ var lingoJsxAttributeScopeInjectMutation = createCodeMutation(
1934
+ (payload) => {
1935
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
1936
+ const jsxAttributeScopes = collectJsxAttributeScopes(payload.ast);
1937
+ for (const [jsxScope, attributes] of jsxAttributeScopes) {
1938
+ const packagePath = mode === "client" ? "lingo.dev/react/client" /* ReactClient */ : "lingo.dev/react/rsc" /* ReactRSC */;
1939
+ const lingoComponentImport = getOrCreateImport(payload.ast, {
1940
+ moduleName: packagePath,
1941
+ exportedName: "LingoAttributeComponent"
1942
+ });
1943
+ const originalJsxElementName = getJsxElementName(jsxScope);
1944
+ if (!originalJsxElementName) {
1945
+ continue;
1946
+ }
1947
+ jsxScope.node.openingElement.name = t17.jsxIdentifier(
1948
+ lingoComponentImport.importedName
1949
+ );
1950
+ if (jsxScope.node.closingElement) {
1951
+ jsxScope.node.closingElement.name = t17.jsxIdentifier(
1952
+ lingoComponentImport.importedName
1953
+ );
1954
+ }
1955
+ const as = /^[A-Z]/.test(originalJsxElementName) ? t17.jsxExpressionContainer(t17.identifier(originalJsxElementName)) : t17.stringLiteral(originalJsxElementName);
1956
+ jsxScope.node.openingElement.attributes.push(
1957
+ t17.jsxAttribute(t17.jsxIdentifier("$attrAs"), as)
1958
+ );
1959
+ jsxScope.node.openingElement.attributes.push(
1960
+ t17.jsxAttribute(
1961
+ t17.jsxIdentifier("$fileKey"),
1962
+ t17.stringLiteral(payload.fileKey)
1963
+ )
1964
+ );
1965
+ jsxScope.node.openingElement.attributes.push(
1966
+ t17.jsxAttribute(
1967
+ t17.jsxIdentifier("$attributes"),
1968
+ t17.jsxExpressionContainer(
1969
+ t17.objectExpression(
1970
+ attributes.map((attributeDefinition) => {
1971
+ const [attribute, key = ""] = attributeDefinition.split(":");
1972
+ return t17.objectProperty(
1973
+ t17.stringLiteral(attribute),
1974
+ t17.stringLiteral(key)
1975
+ );
1976
+ })
1977
+ )
1978
+ )
1979
+ )
1980
+ );
1981
+ if (mode === "server") {
1982
+ const loadDictionaryImport = getOrCreateImport(payload.ast, {
1983
+ exportedName: "loadDictionary",
1984
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */
1985
+ });
1986
+ jsxScope.node.openingElement.attributes.push(
1987
+ t17.jsxAttribute(
1988
+ t17.jsxIdentifier("$loadDictionary"),
1989
+ t17.jsxExpressionContainer(
1990
+ t17.arrowFunctionExpression(
1991
+ [t17.identifier("locale")],
1992
+ t17.callExpression(
1993
+ t17.identifier(loadDictionaryImport.importedName),
1994
+ [t17.identifier("locale")]
1995
+ )
1996
+ )
1997
+ )
1998
+ )
1999
+ );
2000
+ }
2001
+ }
2002
+ return payload;
2003
+ }
2004
+ );
2005
+
2006
+ // src/jsx-scope-inject.ts
2007
+
2008
+
2009
+
2010
+ // src/utils/jsx-variables.ts
2011
+
2012
+ var getJsxVariables = (nodePath) => {
2013
+ const variables = /* @__PURE__ */ new Set();
2014
+ nodePath.traverse({
2015
+ JSXOpeningElement(path6) {
2016
+ path6.skip();
2017
+ },
2018
+ JSXExpressionContainer(path6) {
2019
+ if (t18.isIdentifier(path6.node.expression)) {
2020
+ variables.add(path6.node.expression.name);
2021
+ } else if (t18.isMemberExpression(path6.node.expression)) {
2022
+ let current = path6.node.expression;
2023
+ const parts = [];
2024
+ while (t18.isMemberExpression(current)) {
2025
+ if (t18.isIdentifier(current.property)) {
2026
+ if (current.computed) {
2027
+ parts.unshift(`[${current.property.name}]`);
2028
+ } else {
2029
+ parts.unshift(current.property.name);
2030
+ }
2031
+ }
2032
+ current = current.object;
2033
+ }
2034
+ if (t18.isIdentifier(current)) {
2035
+ parts.unshift(current.name);
2036
+ variables.add(parts.join(".").replaceAll(".[", "["));
2037
+ }
2038
+ }
2039
+ path6.skip();
2040
+ }
2041
+ });
2042
+ const properties = Array.from(variables).map(
2043
+ (name) => t18.objectProperty(t18.stringLiteral(name), t18.identifier(name))
2044
+ );
2045
+ const result = t18.objectExpression(properties);
2046
+ return result;
2047
+ };
2048
+
2049
+ // src/utils/jsx-functions.ts
2050
+
2051
+ var getJsxFunctions = (nodePath) => {
2052
+ const functions = /* @__PURE__ */ new Map();
2053
+ let fnCounter = 0;
2054
+ nodePath.traverse({
2055
+ JSXOpeningElement(path6) {
2056
+ path6.skip();
2057
+ },
2058
+ JSXExpressionContainer(path6) {
2059
+ if (t19.isCallExpression(path6.node.expression)) {
2060
+ let key = "";
2061
+ if (t19.isIdentifier(path6.node.expression.callee)) {
2062
+ key = `${path6.node.expression.callee.name}`;
2063
+ } else if (t19.isMemberExpression(path6.node.expression.callee)) {
2064
+ let firstCallee = path6.node.expression.callee;
2065
+ while (t19.isMemberExpression(firstCallee) && t19.isCallExpression(firstCallee.object)) {
2066
+ firstCallee = firstCallee.object.callee;
2067
+ }
2068
+ let current = firstCallee;
2069
+ const parts = [];
2070
+ while (t19.isMemberExpression(current)) {
2071
+ if (t19.isIdentifier(current.property)) {
2072
+ parts.unshift(current.property.name);
2073
+ }
2074
+ current = current.object;
2075
+ }
2076
+ if (t19.isIdentifier(current)) {
2077
+ parts.unshift(current.name);
2078
+ }
2079
+ if (t19.isMemberExpression(firstCallee) && t19.isNewExpression(firstCallee.object) && t19.isIdentifier(firstCallee.object.callee)) {
2080
+ parts.unshift(firstCallee.object.callee.name);
2081
+ }
2082
+ key = parts.join(".");
2083
+ }
2084
+ const existing = _nullishCoalesce(functions.get(key), () => ( []));
2085
+ functions.set(key, [...existing, path6.node.expression]);
2086
+ fnCounter++;
2087
+ }
2088
+ path6.skip();
2089
+ }
2090
+ });
2091
+ const properties = Array.from(functions.entries()).map(
2092
+ ([name, callExpr]) => t19.objectProperty(t19.stringLiteral(name), t19.arrayExpression(callExpr))
2093
+ );
2094
+ return t19.objectExpression(properties);
2095
+ };
2096
+
2097
+ // src/utils/jsx-expressions.ts
2098
+
2099
+ var getJsxExpressions = (nodePath) => {
2100
+ const expressions = [];
2101
+ nodePath.traverse({
2102
+ JSXOpeningElement(path6) {
2103
+ path6.skip();
2104
+ },
2105
+ JSXExpressionContainer(path6) {
2106
+ const expr = path6.node.expression;
2107
+ if (!t20.isJSXEmptyExpression(expr) && !t20.isIdentifier(expr) && !t20.isMemberExpression(expr) && !t20.isCallExpression(expr) && !(t20.isStringLiteral(expr) && expr.value === " ")) {
2108
+ expressions.push(expr);
2109
+ }
2110
+ path6.skip();
2111
+ }
2112
+ });
2113
+ return t20.arrayExpression(expressions);
2114
+ };
2115
+
2116
+ // src/jsx-scope-inject.ts
2117
+ var lingoJsxScopeInjectMutation = createCodeMutation((payload) => {
2118
+ const mode = getModuleExecutionMode(payload.ast, payload.params.rsc);
2119
+ const jsxScopes = collectJsxScopes(payload.ast);
2120
+ for (const jsxScope of jsxScopes) {
2121
+ const skip = getJsxAttributeValue(jsxScope, "data-lingo-skip");
2122
+ if (skip) {
2123
+ continue;
2124
+ }
2125
+ const packagePath = mode === "client" ? "lingo.dev/react/client" /* ReactClient */ : "lingo.dev/react/rsc" /* ReactRSC */;
2126
+ const lingoComponentImport = getOrCreateImport(payload.ast, {
2127
+ moduleName: packagePath,
2128
+ exportedName: "LingoComponent"
2129
+ });
2130
+ const originalJsxScope = _lodash2.default.cloneDeep(jsxScope);
2131
+ const originalJsxElementName = getJsxElementName(jsxScope);
2132
+ if (!originalJsxElementName) {
2133
+ continue;
2134
+ }
2135
+ jsxScope.node.openingElement.name = t21.jsxIdentifier(
2136
+ lingoComponentImport.importedName
2137
+ );
2138
+ if (jsxScope.node.closingElement) {
2139
+ jsxScope.node.closingElement = null;
2140
+ jsxScope.node.children = [];
2141
+ jsxScope.node.selfClosing = true;
2142
+ jsxScope.node.openingElement.selfClosing = true;
2143
+ }
2144
+ const as = /^[A-Z]/.test(originalJsxElementName) ? t21.jsxExpressionContainer(t21.identifier(originalJsxElementName)) : t21.stringLiteral(originalJsxElementName);
2145
+ jsxScope.node.openingElement.attributes.push(
2146
+ t21.jsxAttribute(t21.jsxIdentifier("$as"), as)
2147
+ );
2148
+ jsxScope.node.openingElement.attributes.push(
2149
+ t21.jsxAttribute(
2150
+ t21.jsxIdentifier("$fileKey"),
2151
+ t21.stringLiteral(payload.fileKey)
2152
+ )
2153
+ );
2154
+ jsxScope.node.openingElement.attributes.push(
2155
+ t21.jsxAttribute(
2156
+ t21.jsxIdentifier("$entryKey"),
2157
+ t21.stringLiteral(getJsxScopeAttribute(jsxScope))
2158
+ )
2159
+ );
2160
+ const $variables = getJsxVariables(originalJsxScope);
2161
+ if ($variables.properties.length > 0) {
2162
+ jsxScope.node.openingElement.attributes.push(
2163
+ t21.jsxAttribute(
2164
+ t21.jsxIdentifier("$variables"),
2165
+ t21.jsxExpressionContainer($variables)
2166
+ )
2167
+ );
2168
+ }
2169
+ const $elements = getNestedJsxElements(originalJsxScope);
2170
+ if ($elements.elements.length > 0) {
2171
+ jsxScope.node.openingElement.attributes.push(
2172
+ t21.jsxAttribute(
2173
+ t21.jsxIdentifier("$elements"),
2174
+ t21.jsxExpressionContainer($elements)
2175
+ )
2176
+ );
2177
+ }
2178
+ const $functions = getJsxFunctions(originalJsxScope);
2179
+ if ($functions.properties.length > 0) {
2180
+ jsxScope.node.openingElement.attributes.push(
2181
+ t21.jsxAttribute(
2182
+ t21.jsxIdentifier("$functions"),
2183
+ t21.jsxExpressionContainer($functions)
2184
+ )
2185
+ );
2186
+ }
2187
+ const $expressions = getJsxExpressions(originalJsxScope);
2188
+ if ($expressions.elements.length > 0) {
2189
+ jsxScope.node.openingElement.attributes.push(
2190
+ t21.jsxAttribute(
2191
+ t21.jsxIdentifier("$expressions"),
2192
+ t21.jsxExpressionContainer($expressions)
2193
+ )
2194
+ );
2195
+ }
2196
+ if (mode === "server") {
2197
+ const loadDictionaryImport = getOrCreateImport(payload.ast, {
2198
+ exportedName: "loadDictionary",
2199
+ moduleName: "lingo.dev/react/rsc" /* ReactRSC */
2200
+ });
2201
+ jsxScope.node.openingElement.attributes.push(
2202
+ t21.jsxAttribute(
2203
+ t21.jsxIdentifier("$loadDictionary"),
2204
+ t21.jsxExpressionContainer(
2205
+ t21.arrowFunctionExpression(
2206
+ [t21.identifier("locale")],
2207
+ t21.callExpression(
2208
+ t21.identifier(loadDictionaryImport.importedName),
2209
+ [t21.identifier("locale")]
2210
+ )
2211
+ )
2212
+ )
2213
+ )
2214
+ );
2215
+ }
2216
+ }
2217
+ return payload;
2218
+ });
2219
+
2220
+ // src/jsx-remove-attributes.ts
2221
+
2222
+
2223
+ var jsxRemoveAttributesMutation = createCodeMutation(
2224
+ (payload) => {
2225
+ const ATTRIBUTES_TO_REMOVE = [
2226
+ "data-jsx-root",
2227
+ "data-jsx-scope",
2228
+ "data-jsx-attribute-scope"
2229
+ ];
2230
+ _traverse2.default.call(void 0, payload.ast, {
2231
+ JSXElement(path6) {
2232
+ const openingElement = path6.node.openingElement;
2233
+ openingElement.attributes = openingElement.attributes.filter((attr) => {
2234
+ const removeAttr = t22.isJSXAttribute(attr) && t22.isJSXIdentifier(attr.name) && ATTRIBUTES_TO_REMOVE.includes(attr.name.name);
2235
+ return !removeAttr;
2236
+ });
2237
+ }
2238
+ });
2239
+ return {
2240
+ ...payload
2241
+ };
2242
+ }
2243
+ );
2244
+
2245
+ // src/client-dictionary-loader.ts
2246
+
2247
+
2248
+ var clientDictionaryLoaderMutation = createCodeMutation((payload) => {
2249
+ const lingoDir = path.default.resolve(
2250
+ process.cwd(),
2251
+ payload.params.sourceRoot,
2252
+ payload.params.lingoDir
2253
+ );
2254
+ const currentDir = path.default.dirname(
2255
+ path.default.resolve(process.cwd(), payload.params.sourceRoot, payload.fileKey)
2256
+ );
2257
+ const relativeLingoPath = path.default.relative(currentDir, lingoDir);
2258
+ const invokations = findInvokations(payload.ast, {
2259
+ moduleName: "lingo.dev/react/client" /* ReactClient */,
2260
+ functionName: "loadDictionary"
2261
+ });
2262
+ const allLocales = Array.from(
2263
+ /* @__PURE__ */ new Set([payload.params.sourceLocale, ...payload.params.targetLocales])
2264
+ );
2265
+ for (const invokation of invokations) {
2266
+ const internalDictionaryLoader = getOrCreateImport(payload.ast, {
2267
+ moduleName: "lingo.dev/react/client" /* ReactClient */,
2268
+ exportedName: "loadDictionary_internal"
2269
+ });
2270
+ if (t23.isIdentifier(invokation.callee)) {
2271
+ invokation.callee.name = internalDictionaryLoader.importedName;
2272
+ }
2273
+ const localeImportMap = t23.objectExpression(
2274
+ allLocales.map(
2275
+ (locale) => t23.objectProperty(
2276
+ t23.identifier(locale),
2277
+ t23.arrowFunctionExpression(
2278
+ [],
2279
+ t23.callExpression(t23.identifier("import"), [
2280
+ t23.stringLiteral(
2281
+ `./${relativeLingoPath}/${LCP_DICTIONARY_FILE_NAME}?locale=${locale}`
2282
+ )
2283
+ ])
2284
+ )
2285
+ )
2286
+ )
2287
+ );
2288
+ invokation.arguments.push(localeImportMap);
2289
+ }
2290
+ return payload;
2291
+ });
2292
+
2293
+ // src/index.ts
2294
+ var unplugin = _unplugin.createUnplugin.call(void 0,
2295
+ (_params, _meta) => {
2296
+ console.log("\u2139\uFE0F Starting Lingo.dev compiler...");
2297
+ if (!isRunningInCIOrDocker()) {
2298
+ validateGroqKeyDetails();
2299
+ }
2300
+ const params = _lodash2.default.defaults(_params, defaultParams);
2301
+ const invalidLocales = getInvalidLocales(
2302
+ params.models,
2303
+ params.sourceLocale,
2304
+ params.targetLocales
2305
+ );
2306
+ if (invalidLocales.length > 0) {
2307
+ console.log(_dedent2.default`
2308
+ \n
2309
+ ⚠️ Lingo.dev Localization Compiler requires LLM model setup for the following locales: ${invalidLocales.join(", ")}.
2310
+
2311
+ ⭐️ Next steps:
2312
+ 1. Refer to documentation for help: https://docs.lingo.dev/
2313
+ 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2314
+ 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2315
+
2316
+
2317
+ `);
2318
+ process.exit(1);
2319
+ }
2320
+ LCPCache.ensureDictionaryFile({
2321
+ sourceRoot: params.sourceRoot,
2322
+ lingoDir: params.lingoDir
2323
+ });
2324
+ return {
2325
+ name: package_default.name,
2326
+ loadInclude: (id) => !!id.match(LCP_DICTIONARY_FILE_NAME),
2327
+ async load(id) {
2328
+ const moduleInfo = parseParametrizedModuleId(id);
2329
+ const lcpParams = {
2330
+ sourceRoot: params.sourceRoot,
2331
+ lingoDir: params.lingoDir
2332
+ };
2333
+ await LCP.ready(lcpParams);
2334
+ const lcp = LCP.getInstance(lcpParams);
2335
+ const dictionaries = await LCPServer.loadDictionaries({
2336
+ models: params.models,
2337
+ lcp: lcp.data,
2338
+ sourceLocale: params.sourceLocale,
2339
+ targetLocales: params.targetLocales,
2340
+ sourceRoot: params.sourceRoot,
2341
+ lingoDir: params.lingoDir
2342
+ });
2343
+ const dictionary = dictionaries[moduleInfo.params.locale];
2344
+ return {
2345
+ code: `export default ${JSON.stringify(dictionary, null, 2)}`
2346
+ };
2347
+ },
2348
+ transformInclude: (id) => id.endsWith(".tsx") || id.endsWith(".jsx"),
2349
+ enforce: "pre",
2350
+ transform(code, id) {
2351
+ try {
2352
+ const result = _lodash2.default.chain({
2353
+ code,
2354
+ params,
2355
+ fileKey: path.default.relative(
2356
+ path.default.resolve(process.cwd(), params.sourceRoot),
2357
+ id
2358
+ )
2359
+ }).thru(createPayload).thru(
2360
+ composeMutations(
2361
+ i18n_directive_default,
2362
+ jsxFragmentMutation,
2363
+ jsx_attribute_flag_default,
2364
+ // log here to see transformedfiles
2365
+ // (input) => {
2366
+ // console.log(`transform ${id}`);
2367
+ // return input;
2368
+ // },
2369
+ jsx_provider_default,
2370
+ jsxHtmlLangMutation,
2371
+ jsx_root_flag_default,
2372
+ jsx_scope_flag_default,
2373
+ jsx_attribute_flag_default,
2374
+ jsxAttributeScopesExportMutation,
2375
+ jsxScopesExportMutation,
2376
+ lingoJsxAttributeScopeInjectMutation,
2377
+ lingoJsxScopeInjectMutation,
2378
+ rscDictionaryLoaderMutation,
2379
+ reactRouterDictionaryLoaderMutation,
2380
+ jsxRemoveAttributesMutation,
2381
+ clientDictionaryLoaderMutation
2382
+ )
2383
+ ).thru(createOutput).value();
2384
+ return result;
2385
+ } catch (error) {
2386
+ console.error("\u26A0\uFE0F Lingo.dev compiler failed to localize your app");
2387
+ console.error("\u26A0\uFE0F Details:", error);
2388
+ return code;
2389
+ }
2390
+ }
2391
+ };
2392
+ }
2393
+ );
2394
+ var src_default = {
2395
+ next: (compilerParams) => (nextConfig) => ({
2396
+ ...nextConfig,
2397
+ // what if we already have a webpack config?
2398
+ webpack: (config2, { isServer }) => {
2399
+ config2.plugins.push(
2400
+ unplugin.webpack(
2401
+ _lodash2.default.merge({}, defaultParams, { rsc: true }, compilerParams)
2402
+ )
2403
+ );
2404
+ return config2;
2405
+ }
2406
+ }),
2407
+ vite: (compilerParams) => (config2) => {
2408
+ config2.plugins.push(
2409
+ unplugin.vite(_lodash2.default.merge({}, defaultParams, { rsc: false }, compilerParams))
2410
+ );
2411
+ return config2;
2412
+ }
2413
+ };
2414
+ function validateGroqKeyDetails() {
2415
+ const groq = {
2416
+ fromEnv: getGroqKeyFromEnv(),
2417
+ fromRc: getGroqKeyFromRc()
2418
+ };
2419
+ if (!groq.fromEnv && !groq.fromRc) {
2420
+ console.log(_dedent2.default`
2421
+ \n
2422
+ 💡 You're using Lingo.dev Localization Compiler in your project, which requires a GROQ API key to work.
2423
+
2424
+ 👉 You can set the API key in one of the following ways:
2425
+ 1. User-wide: Run npx lingo.dev@latest config set llm.groqApiKey <your-api-key>
2426
+ 2. Project-wide: Add GROQ_API_KEY=<your-api-key> to .env file in every project that uses Lingo.dev Localization Compiler
2427
+ 3. Session-wide: Run export GROQ_API_KEY=<your-api-key> in your terminal before running the compiler to set the API key for the current session
2428
+
2429
+ ⭐️ Also:
2430
+ 1. If you don't yet have a GROQ API key, get one for free at https://groq.com
2431
+ 2. If you want to use a different LLM, raise an issue in our open-source repo: https://lingo.dev/go/gh
2432
+ 3. If you have questions, feature requests, or would like to contribute, join our Discord: https://lingo.dev/go/discord
2433
+
2434
+
2435
+ `);
2436
+ process.exit(1);
2437
+ } else if (groq.fromEnv && groq.fromRc) {
2438
+ console.log(
2439
+ _dedent2.default`
2440
+ 🔑 GROQ API key detected in both environment variables and your user-wide configuration.
2441
+
2442
+ 👉 The compiler will use the key from the environment because it has higher priority.
2443
+
2444
+ • To update the user-wide key run: npx lingo.dev@latest config set llm.groqApiKey <your-api-key>
2445
+ • To remove it run: npx lingo.dev@latest config unset llm.groqApiKey
2446
+ • To remove the env variable from the current session run: unset GROQ_API_KEY
2447
+ `
2448
+ );
2449
+ } else if (groq.fromEnv && !groq.fromRc) {
2450
+ console.log(
2451
+ _dedent2.default`
2452
+ 🔑 GROQ API key loaded from environment variables.
2453
+
2454
+ • You can also save the key user-wide with: npx lingo.dev@latest config set llm.groqApiKey <your-api-key>
2455
+ • Or remove the env variable from the current session with: unset GROQ_API_KEY
2456
+ `
2457
+ );
2458
+ } else if (!groq.fromEnv && groq.fromRc) {
2459
+ console.log(
2460
+ _dedent2.default`
2461
+ 🔑 GROQ API key loaded from your user-wide configuration.
2462
+ `
2463
+ );
2464
+ }
2465
+ }
2466
+
2467
+
2468
+ exports.default = src_default;
2469
+
2470
+ module.exports = exports.default;