@lingo.dev/compiler 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +201 -0
- package/README.md +192 -0
- package/build/_virtual/rolldown_runtime.cjs +29 -0
- package/build/_virtual/rolldown_runtime.mjs +7 -0
- package/build/index.cjs +0 -0
- package/build/index.d.cts +2 -0
- package/build/index.d.mts +2 -0
- package/build/index.mjs +1 -0
- package/build/metadata/manager.cjs +131 -0
- package/build/metadata/manager.mjs +123 -0
- package/build/metadata/manager.mjs.map +1 -0
- package/build/plugin/build-translator.cjs +198 -0
- package/build/plugin/build-translator.mjs +196 -0
- package/build/plugin/build-translator.mjs.map +1 -0
- package/build/plugin/cleanup.cjs +20 -0
- package/build/plugin/cleanup.mjs +20 -0
- package/build/plugin/cleanup.mjs.map +1 -0
- package/build/plugin/next-compiler-loader.cjs +41 -0
- package/build/plugin/next-compiler-loader.d.cts +12 -0
- package/build/plugin/next-compiler-loader.d.cts.map +1 -0
- package/build/plugin/next-compiler-loader.d.mts +13 -0
- package/build/plugin/next-compiler-loader.d.mts.map +1 -0
- package/build/plugin/next-compiler-loader.mjs +42 -0
- package/build/plugin/next-compiler-loader.mjs.map +1 -0
- package/build/plugin/next-config-loader.cjs +13 -0
- package/build/plugin/next-config-loader.d.cts +8 -0
- package/build/plugin/next-config-loader.d.cts.map +1 -0
- package/build/plugin/next-config-loader.d.mts +9 -0
- package/build/plugin/next-config-loader.d.mts.map +1 -0
- package/build/plugin/next-config-loader.mjs +14 -0
- package/build/plugin/next-config-loader.mjs.map +1 -0
- package/build/plugin/next-locale-client-loader.cjs +9 -0
- package/build/plugin/next-locale-client-loader.d.cts +8 -0
- package/build/plugin/next-locale-client-loader.d.cts.map +1 -0
- package/build/plugin/next-locale-client-loader.d.mts +9 -0
- package/build/plugin/next-locale-client-loader.d.mts.map +1 -0
- package/build/plugin/next-locale-client-loader.mjs +10 -0
- package/build/plugin/next-locale-client-loader.mjs.map +1 -0
- package/build/plugin/next-locale-server-loader.cjs +9 -0
- package/build/plugin/next-locale-server-loader.d.cts +8 -0
- package/build/plugin/next-locale-server-loader.d.cts.map +1 -0
- package/build/plugin/next-locale-server-loader.d.mts +9 -0
- package/build/plugin/next-locale-server-loader.d.mts.map +1 -0
- package/build/plugin/next-locale-server-loader.mjs +10 -0
- package/build/plugin/next-locale-server-loader.mjs.map +1 -0
- package/build/plugin/next.cjs +220 -0
- package/build/plugin/next.d.cts +9 -0
- package/build/plugin/next.d.cts.map +1 -0
- package/build/plugin/next.d.mts +9 -0
- package/build/plugin/next.d.mts.map +1 -0
- package/build/plugin/next.mjs +222 -0
- package/build/plugin/next.mjs.map +1 -0
- package/build/plugin/transform/babel-compat.cjs +13 -0
- package/build/plugin/transform/babel-compat.mjs +10 -0
- package/build/plugin/transform/babel-compat.mjs.map +1 -0
- package/build/plugin/transform/index.cjs +44 -0
- package/build/plugin/transform/index.mjs +42 -0
- package/build/plugin/transform/index.mjs.map +1 -0
- package/build/plugin/transform/metadata.cjs +142 -0
- package/build/plugin/transform/metadata.mjs +141 -0
- package/build/plugin/transform/metadata.mjs.map +1 -0
- package/build/plugin/transform/parse-override.cjs +145 -0
- package/build/plugin/transform/parse-override.mjs +144 -0
- package/build/plugin/transform/parse-override.mjs.map +1 -0
- package/build/plugin/transform/process-file.cjs +391 -0
- package/build/plugin/transform/process-file.mjs +390 -0
- package/build/plugin/transform/process-file.mjs.map +1 -0
- package/build/plugin/transform/use-i18n.cjs +8 -0
- package/build/plugin/transform/use-i18n.mjs +7 -0
- package/build/plugin/transform/use-i18n.mjs.map +1 -0
- package/build/plugin/transform/utils.cjs +205 -0
- package/build/plugin/transform/utils.mjs +192 -0
- package/build/plugin/transform/utils.mjs.map +1 -0
- package/build/plugin/unplugin.cjs +188 -0
- package/build/plugin/unplugin.d.cts +8 -0
- package/build/plugin/unplugin.d.cts.map +1 -0
- package/build/plugin/unplugin.d.mts +8 -0
- package/build/plugin/unplugin.d.mts.map +1 -0
- package/build/plugin/unplugin.mjs +186 -0
- package/build/plugin/unplugin.mjs.map +1 -0
- package/build/plugin/vite.cjs +28 -0
- package/build/plugin/vite.d.cts +9 -0
- package/build/plugin/vite.d.cts.map +1 -0
- package/build/plugin/vite.d.mts +9 -0
- package/build/plugin/vite.d.mts.map +1 -0
- package/build/plugin/vite.mjs +29 -0
- package/build/plugin/vite.mjs.map +1 -0
- package/build/plugin/webpack.cjs +27 -0
- package/build/plugin/webpack.d.cts +8 -0
- package/build/plugin/webpack.d.cts.map +1 -0
- package/build/plugin/webpack.d.mts +8 -0
- package/build/plugin/webpack.d.mts.map +1 -0
- package/build/plugin/webpack.mjs +28 -0
- package/build/plugin/webpack.mjs.map +1 -0
- package/build/react/client/index.cjs +9 -0
- package/build/react/client/index.d.cts +5 -0
- package/build/react/client/index.d.mts +5 -0
- package/build/react/client/index.mjs +6 -0
- package/build/react/client/useTranslation.cjs +71 -0
- package/build/react/client/useTranslation.d.cts +42 -0
- package/build/react/client/useTranslation.d.cts.map +1 -0
- package/build/react/client/useTranslation.d.mts +42 -0
- package/build/react/client/useTranslation.d.mts.map +1 -0
- package/build/react/client/useTranslation.mjs +71 -0
- package/build/react/client/useTranslation.mjs.map +1 -0
- package/build/react/next/client.cjs +25 -0
- package/build/react/next/client.d.cts +9 -0
- package/build/react/next/client.d.cts.map +1 -0
- package/build/react/next/client.d.mts +9 -0
- package/build/react/next/client.d.mts.map +1 -0
- package/build/react/next/client.mjs +24 -0
- package/build/react/next/client.mjs.map +1 -0
- package/build/react/next/cookie-locale-resolver.cjs +29 -0
- package/build/react/next/cookie-locale-resolver.d.cts +33 -0
- package/build/react/next/cookie-locale-resolver.d.cts.map +1 -0
- package/build/react/next/cookie-locale-resolver.d.mts +33 -0
- package/build/react/next/cookie-locale-resolver.d.mts.map +1 -0
- package/build/react/next/cookie-locale-resolver.mjs +29 -0
- package/build/react/next/cookie-locale-resolver.mjs.map +1 -0
- package/build/react/next/server.cjs +21 -0
- package/build/react/next/server.d.cts +13 -0
- package/build/react/next/server.d.cts.map +1 -0
- package/build/react/next/server.d.mts +14 -0
- package/build/react/next/server.d.mts.map +1 -0
- package/build/react/next/server.mjs +20 -0
- package/build/react/next/server.mjs.map +1 -0
- package/build/react/server/ServerLingoProvider.cjs +19 -0
- package/build/react/server/ServerLingoProvider.d.cts +12 -0
- package/build/react/server/ServerLingoProvider.d.cts.map +1 -0
- package/build/react/server/ServerLingoProvider.d.mts +12 -0
- package/build/react/server/ServerLingoProvider.d.mts.map +1 -0
- package/build/react/server/ServerLingoProvider.mjs +19 -0
- package/build/react/server/ServerLingoProvider.mjs.map +1 -0
- package/build/react/server/index.cjs +7 -0
- package/build/react/server/index.d.cts +4 -0
- package/build/react/server/index.d.mts +4 -0
- package/build/react/server/index.mjs +5 -0
- package/build/react/server/useTranslation.cjs +60 -0
- package/build/react/server/useTranslation.d.cts +36 -0
- package/build/react/server/useTranslation.d.cts.map +1 -0
- package/build/react/server/useTranslation.d.mts +36 -0
- package/build/react/server/useTranslation.d.mts.map +1 -0
- package/build/react/server/useTranslation.mjs +60 -0
- package/build/react/server/useTranslation.mjs.map +1 -0
- package/build/react/server-only/index.cjs +42 -0
- package/build/react/server-only/index.d.cts +38 -0
- package/build/react/server-only/index.d.cts.map +1 -0
- package/build/react/server-only/index.d.mts +38 -0
- package/build/react/server-only/index.d.mts.map +1 -0
- package/build/react/server-only/index.mjs +42 -0
- package/build/react/server-only/index.mjs.map +1 -0
- package/build/react/server-only/translations.cjs +85 -0
- package/build/react/server-only/translations.mjs +85 -0
- package/build/react/server-only/translations.mjs.map +1 -0
- package/build/react/shared/LingoContext.cjs +14 -0
- package/build/react/shared/LingoContext.d.cts +41 -0
- package/build/react/shared/LingoContext.d.cts.map +1 -0
- package/build/react/shared/LingoContext.d.mts +41 -0
- package/build/react/shared/LingoContext.d.mts.map +1 -0
- package/build/react/shared/LingoContext.mjs +13 -0
- package/build/react/shared/LingoContext.mjs.map +1 -0
- package/build/react/shared/LingoProvider.cjs +274 -0
- package/build/react/shared/LingoProvider.d.cts +76 -0
- package/build/react/shared/LingoProvider.d.cts.map +1 -0
- package/build/react/shared/LingoProvider.d.mts +76 -0
- package/build/react/shared/LingoProvider.d.mts.map +1 -0
- package/build/react/shared/LingoProvider.mjs +274 -0
- package/build/react/shared/LingoProvider.mjs.map +1 -0
- package/build/react/shared/LocaleSwitcher.cjs +61 -0
- package/build/react/shared/LocaleSwitcher.d.cts +71 -0
- package/build/react/shared/LocaleSwitcher.d.cts.map +1 -0
- package/build/react/shared/LocaleSwitcher.d.mts +71 -0
- package/build/react/shared/LocaleSwitcher.d.mts.map +1 -0
- package/build/react/shared/LocaleSwitcher.mjs +61 -0
- package/build/react/shared/LocaleSwitcher.mjs.map +1 -0
- package/build/react/shared/render-rich-text.cjs +55 -0
- package/build/react/shared/render-rich-text.d.cts +17 -0
- package/build/react/shared/render-rich-text.d.cts.map +1 -0
- package/build/react/shared/render-rich-text.d.mts +17 -0
- package/build/react/shared/render-rich-text.d.mts.map +1 -0
- package/build/react/shared/render-rich-text.mjs +54 -0
- package/build/react/shared/render-rich-text.mjs.map +1 -0
- package/build/react/shared/utils.cjs +34 -0
- package/build/react/shared/utils.mjs +35 -0
- package/build/react/shared/utils.mjs.map +1 -0
- package/build/react/types.d.cts +16 -0
- package/build/react/types.d.cts.map +1 -0
- package/build/react/types.d.mts +16 -0
- package/build/react/types.d.mts.map +1 -0
- package/build/translation-server/logger.cjs +37 -0
- package/build/translation-server/logger.mjs +37 -0
- package/build/translation-server/logger.mjs.map +1 -0
- package/build/translation-server/translation-server.cjs +547 -0
- package/build/translation-server/translation-server.mjs +544 -0
- package/build/translation-server/translation-server.mjs.map +1 -0
- package/build/translation-server/ws-events.cjs +15 -0
- package/build/translation-server/ws-events.mjs +15 -0
- package/build/translation-server/ws-events.mjs.map +1 -0
- package/build/translators/api.cjs +12 -0
- package/build/translators/api.mjs +12 -0
- package/build/translators/api.mjs.map +1 -0
- package/build/translators/cache-factory.cjs +26 -0
- package/build/translators/cache-factory.mjs +27 -0
- package/build/translators/cache-factory.mjs.map +1 -0
- package/build/translators/lingo/model-factory.cjs +179 -0
- package/build/translators/lingo/model-factory.mjs +174 -0
- package/build/translators/lingo/model-factory.mjs.map +1 -0
- package/build/translators/lingo/prompt.cjs +43 -0
- package/build/translators/lingo/prompt.mjs +43 -0
- package/build/translators/lingo/prompt.mjs.map +1 -0
- package/build/translators/lingo/service.cjs +152 -0
- package/build/translators/lingo/service.mjs +152 -0
- package/build/translators/lingo/service.mjs.map +1 -0
- package/build/translators/lingo/shots.cjs +28 -0
- package/build/translators/lingo/shots.mjs +28 -0
- package/build/translators/lingo/shots.mjs.map +1 -0
- package/build/translators/local-cache.cjs +115 -0
- package/build/translators/local-cache.mjs +113 -0
- package/build/translators/local-cache.mjs.map +1 -0
- package/build/translators/parse-xml.cjs +109 -0
- package/build/translators/parse-xml.mjs +108 -0
- package/build/translators/parse-xml.mjs.map +1 -0
- package/build/translators/pluralization/icu-validator.cjs +36 -0
- package/build/translators/pluralization/icu-validator.mjs +36 -0
- package/build/translators/pluralization/icu-validator.mjs.map +1 -0
- package/build/translators/pluralization/pattern-detector.cjs +25 -0
- package/build/translators/pluralization/pattern-detector.mjs +25 -0
- package/build/translators/pluralization/pattern-detector.mjs.map +1 -0
- package/build/translators/pluralization/prompt.cjs +98 -0
- package/build/translators/pluralization/prompt.mjs +98 -0
- package/build/translators/pluralization/prompt.mjs.map +1 -0
- package/build/translators/pluralization/service.cjs +247 -0
- package/build/translators/pluralization/service.mjs +247 -0
- package/build/translators/pluralization/service.mjs.map +1 -0
- package/build/translators/pluralization/shots.cjs +53 -0
- package/build/translators/pluralization/shots.mjs +53 -0
- package/build/translators/pluralization/shots.mjs.map +1 -0
- package/build/translators/pluralization/types.d.cts +17 -0
- package/build/translators/pluralization/types.d.cts.map +1 -0
- package/build/translators/pluralization/types.d.mts +17 -0
- package/build/translators/pluralization/types.d.mts.map +1 -0
- package/build/translators/pseudotranslator/index.cjs +129 -0
- package/build/translators/pseudotranslator/index.mjs +129 -0
- package/build/translators/pseudotranslator/index.mjs.map +1 -0
- package/build/translators/translation-service.cjs +182 -0
- package/build/translators/translation-service.mjs +183 -0
- package/build/translators/translation-service.mjs.map +1 -0
- package/build/translators/translator-factory.cjs +49 -0
- package/build/translators/translator-factory.mjs +50 -0
- package/build/translators/translator-factory.mjs.map +1 -0
- package/build/types.d.cts +161 -0
- package/build/types.d.cts.map +1 -0
- package/build/types.d.mts +161 -0
- package/build/types.d.mts.map +1 -0
- package/build/utils/config-factory.cjs +58 -0
- package/build/utils/config-factory.mjs +58 -0
- package/build/utils/config-factory.mjs.map +1 -0
- package/build/utils/hash.cjs +17 -0
- package/build/utils/hash.mjs +16 -0
- package/build/utils/hash.mjs.map +1 -0
- package/build/utils/is-valid-locale.cjs +14 -0
- package/build/utils/is-valid-locale.mjs +14 -0
- package/build/utils/is-valid-locale.mjs.map +1 -0
- package/build/utils/logger.cjs +51 -0
- package/build/utils/logger.mjs +50 -0
- package/build/utils/logger.mjs.map +1 -0
- package/build/utils/path-helpers.cjs +49 -0
- package/build/utils/path-helpers.mjs +47 -0
- package/build/utils/path-helpers.mjs.map +1 -0
- package/build/utils/timeout.cjs +42 -0
- package/build/utils/timeout.mjs +41 -0
- package/build/utils/timeout.mjs.map +1 -0
- package/build/virtual/code-generator.cjs +54 -0
- package/build/virtual/code-generator.mjs +53 -0
- package/build/virtual/code-generator.mjs.map +1 -0
- package/build/virtual/config.cjs +10 -0
- package/build/virtual/config.d.cts +9 -0
- package/build/virtual/config.d.cts.map +1 -0
- package/build/virtual/config.d.mts +9 -0
- package/build/virtual/config.d.mts.map +1 -0
- package/build/virtual/config.mjs +8 -0
- package/build/virtual/config.mjs.map +1 -0
- package/build/virtual/locale/client.cjs +23 -0
- package/build/virtual/locale/client.d.cts +19 -0
- package/build/virtual/locale/client.d.cts.map +1 -0
- package/build/virtual/locale/client.d.mts +19 -0
- package/build/virtual/locale/client.d.mts.map +1 -0
- package/build/virtual/locale/client.mjs +22 -0
- package/build/virtual/locale/client.mjs.map +1 -0
- package/build/virtual/locale/server.cjs +13 -0
- package/build/virtual/locale/server.d.cts +13 -0
- package/build/virtual/locale/server.d.cts.map +1 -0
- package/build/virtual/locale/server.d.mts +13 -0
- package/build/virtual/locale/server.d.mts.map +1 -0
- package/build/virtual/locale/server.mjs +13 -0
- package/build/virtual/locale/server.mjs.map +1 -0
- package/build/widget/lingo-dev-widget.cjs +228 -0
- package/build/widget/lingo-dev-widget.mjs +229 -0
- package/build/widget/lingo-dev-widget.mjs.map +1 -0
- package/package.json +189 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { logger } from "../../utils/logger.mjs";
|
|
2
|
+
import { comstructUnifiedImport, constructServerImport, constructTranslationCall, createTranslationEntry, escapeTextForICU, hasUseI18nDirective, inferComponentName, injectServerHook, injectUnifiedHook, isReactComponent, isVoidElement, normalizeWhitespace } from "./utils.mjs";
|
|
3
|
+
import { processNextDynamicMetadata, processNextStaticMetadata } from "./metadata.mjs";
|
|
4
|
+
import { traverse } from "./babel-compat.mjs";
|
|
5
|
+
import { processOverrideAttributes } from "./parse-override.mjs";
|
|
6
|
+
import * as t from "@babel/types";
|
|
7
|
+
|
|
8
|
+
//#region src/plugin/transform/process-file.ts
|
|
9
|
+
/**
|
|
10
|
+
* Keep mutations to the current node and children. Do not mutate program inserting imports when processing the component.
|
|
11
|
+
* This should be done in the end.
|
|
12
|
+
* The overall idea is that state is used for side effect tracking. When we exit relevant points, we check if we need to make extra changes at this level.
|
|
13
|
+
* e.g. when we transform elements to insert translations, we record their hashes and add the translation hook call when leaving the component.
|
|
14
|
+
*/
|
|
15
|
+
function updateWithMetadataState(state, newState) {
|
|
16
|
+
state.newEntries = [...state.newEntries, ...newState.newEntries];
|
|
17
|
+
state.needsAsyncImport = state.needsAsyncImport || newState.needsAsyncImport;
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
const SKIP_ATTRIBUTE = "data-lingo-skip";
|
|
21
|
+
/**
|
|
22
|
+
* Elements whose content should not be translated by default.
|
|
23
|
+
* These are typically code-related or technical elements where translation
|
|
24
|
+
* would break functionality or meaning.
|
|
25
|
+
*/
|
|
26
|
+
const NON_TRANSLATABLE_ELEMENTS = new Set([
|
|
27
|
+
"code",
|
|
28
|
+
"pre",
|
|
29
|
+
"script",
|
|
30
|
+
"style",
|
|
31
|
+
"kbd",
|
|
32
|
+
"samp",
|
|
33
|
+
"var"
|
|
34
|
+
]);
|
|
35
|
+
const TRANSLATABLE_ATTRIBUTES = new Set([
|
|
36
|
+
"title",
|
|
37
|
+
"aria-label",
|
|
38
|
+
"aria-description",
|
|
39
|
+
"alt",
|
|
40
|
+
"label",
|
|
41
|
+
"description",
|
|
42
|
+
"placeholder",
|
|
43
|
+
"content",
|
|
44
|
+
"subtitle"
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Check if a JSX element should skip translation based on:
|
|
48
|
+
* 1. Element type (code, pre, script, style, etc.)
|
|
49
|
+
* 2. translate="no" attribute (HTML standard)
|
|
50
|
+
* 3. data-lingo-skip attribute
|
|
51
|
+
*/
|
|
52
|
+
function shouldSkipTranslationForElement(element) {
|
|
53
|
+
if (element.type === "JSXFragment") return false;
|
|
54
|
+
const openingElement = element.openingElement;
|
|
55
|
+
if (openingElement.name.type === "JSXIdentifier") {
|
|
56
|
+
if (NON_TRANSLATABLE_ELEMENTS.has(openingElement.name.name)) return true;
|
|
57
|
+
}
|
|
58
|
+
for (const attr of openingElement.attributes) if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier") {
|
|
59
|
+
if (attr.name.name === "translate") {
|
|
60
|
+
if (attr.value?.type === "StringLiteral" && attr.value.value === "no") return true;
|
|
61
|
+
}
|
|
62
|
+
if (attr.name.name === SKIP_ATTRIBUTE) return true;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
function translateAttributes(node, state) {
|
|
67
|
+
const openingElement = node.openingElement;
|
|
68
|
+
const component = state.componentsStack.at(-1);
|
|
69
|
+
if (!component) return;
|
|
70
|
+
for (const attr of openingElement.attributes) if (attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && TRANSLATABLE_ATTRIBUTES.has(attr.name.name) && attr.value?.type === "StringLiteral") {
|
|
71
|
+
const text = normalizeWhitespace(attr.value.value);
|
|
72
|
+
if (text.length == 0) continue;
|
|
73
|
+
const entry = createTranslationEntry("attribute", text, {
|
|
74
|
+
attributeName: attr.name.name,
|
|
75
|
+
componentName: component.name
|
|
76
|
+
}, state.filePath, node.loc?.start.line, node.loc?.start.column);
|
|
77
|
+
registerEntry(entry, state, component.name);
|
|
78
|
+
attr.value = constructTranslationCall(entry.hash, text);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if a JSX element has mixed content that needs rich text translation
|
|
83
|
+
* Mixed content = text nodes + expressions + nested JSX elements (excluding void elements)
|
|
84
|
+
*/
|
|
85
|
+
function hasMixedContent(element) {
|
|
86
|
+
const children = element.children;
|
|
87
|
+
if (children.length === 0) return false;
|
|
88
|
+
let state = 0;
|
|
89
|
+
for (const child of children) if (child.type === "JSXText") {
|
|
90
|
+
if (child.value.trim().length > 0) switch (state) {
|
|
91
|
+
case 0:
|
|
92
|
+
state = 1;
|
|
93
|
+
break;
|
|
94
|
+
case 1: break;
|
|
95
|
+
case 2: return true;
|
|
96
|
+
case 3: return true;
|
|
97
|
+
}
|
|
98
|
+
} else if (child.type === "JSXExpressionContainer") switch (state) {
|
|
99
|
+
case 0:
|
|
100
|
+
state = 2;
|
|
101
|
+
break;
|
|
102
|
+
case 1: return true;
|
|
103
|
+
case 2: break;
|
|
104
|
+
case 3: return true;
|
|
105
|
+
}
|
|
106
|
+
else if (child.type === "JSXElement") if (!isVoidElement(child)) switch (state) {
|
|
107
|
+
case 0:
|
|
108
|
+
state = 2;
|
|
109
|
+
break;
|
|
110
|
+
case 1: return true;
|
|
111
|
+
case 2: break;
|
|
112
|
+
case 3: return true;
|
|
113
|
+
}
|
|
114
|
+
else switch (state) {
|
|
115
|
+
case 0: break;
|
|
116
|
+
case 1:
|
|
117
|
+
state = 3;
|
|
118
|
+
break;
|
|
119
|
+
case 2: break;
|
|
120
|
+
case 3: break;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Serialize JSX children to a translation string with placeholders
|
|
126
|
+
*/
|
|
127
|
+
function serializeJSXChildren(children, state) {
|
|
128
|
+
let text = "";
|
|
129
|
+
const variables = [];
|
|
130
|
+
const expressions = /* @__PURE__ */ new Map();
|
|
131
|
+
const components = /* @__PURE__ */ new Map();
|
|
132
|
+
const elementCounts = /* @__PURE__ */ new Map();
|
|
133
|
+
for (const child of children) if (child.type === "JSXText") {
|
|
134
|
+
let normalized = normalizeWhitespace(child.value);
|
|
135
|
+
if (text.length > 0 && child.value.match(/^\s/) && normalized.length > 0) {
|
|
136
|
+
if (!text.endsWith(" ")) normalized = ` ${normalized}`;
|
|
137
|
+
}
|
|
138
|
+
if (normalized.length > 0 && child.value.match(/\s$/)) {
|
|
139
|
+
if (!text.endsWith(" ")) normalized = `${normalized} `;
|
|
140
|
+
}
|
|
141
|
+
text += escapeTextForICU(normalized);
|
|
142
|
+
} else if (child.type === "JSXExpressionContainer") {
|
|
143
|
+
const expr = child.expression;
|
|
144
|
+
if (expr.type === "Identifier") {
|
|
145
|
+
text += `{${expr.name}}`;
|
|
146
|
+
if (!variables.includes(expr.name)) variables.push(expr.name);
|
|
147
|
+
} else if (expr.type === "StringLiteral") text += escapeTextForICU(expr.value);
|
|
148
|
+
else if (expr.type !== "JSXEmptyExpression") {
|
|
149
|
+
const name = `expression${expressions.size}`;
|
|
150
|
+
text += `{${name}}`;
|
|
151
|
+
expressions.set(name, expr);
|
|
152
|
+
}
|
|
153
|
+
} else if (child.type === "JSXElement") {
|
|
154
|
+
let shouldTranslate = true;
|
|
155
|
+
if (isVoidElement(child)) shouldTranslate = false;
|
|
156
|
+
if (shouldSkipTranslationForElement(child)) shouldTranslate = false;
|
|
157
|
+
const openingElement = child.openingElement;
|
|
158
|
+
let elementName = "";
|
|
159
|
+
if (openingElement.name.type === "JSXIdentifier") elementName = openingElement.name.name;
|
|
160
|
+
else elementName = "element";
|
|
161
|
+
const count = elementCounts.get(elementName) || 0;
|
|
162
|
+
elementCounts.set(elementName, count + 1);
|
|
163
|
+
const tagName = `${elementName}${count}`;
|
|
164
|
+
translateAttributes(child, state);
|
|
165
|
+
if (shouldTranslate) {
|
|
166
|
+
const nested = serializeJSXChildren(child.children, state);
|
|
167
|
+
text += `<${tagName}>${nested.text}</${tagName}>`;
|
|
168
|
+
for (const v of nested.variables) if (!variables.includes(v)) variables.push(v);
|
|
169
|
+
for (const [nestedTag, nestedElement] of nested.components) components.set(`${tagName}_${nestedTag}`, nestedElement);
|
|
170
|
+
} else {
|
|
171
|
+
text += `<${tagName}></${tagName}>`;
|
|
172
|
+
child.extra = {
|
|
173
|
+
...child.extra,
|
|
174
|
+
shouldTranslate: false
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
components.set(tagName, child);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
text,
|
|
181
|
+
variables,
|
|
182
|
+
expressions,
|
|
183
|
+
components
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function transformVoidElement(node, state) {
|
|
187
|
+
translateAttributes(node, state);
|
|
188
|
+
}
|
|
189
|
+
function getTranslationScope(node, state) {
|
|
190
|
+
if (hasMixedContent(node)) {
|
|
191
|
+
const serialized = serializeJSXChildren(node.children, state);
|
|
192
|
+
const text$1 = serialized.text.trim();
|
|
193
|
+
if (text$1.length === 0) return null;
|
|
194
|
+
return {
|
|
195
|
+
kind: "mixed",
|
|
196
|
+
text: text$1,
|
|
197
|
+
args: {
|
|
198
|
+
variables: serialized.variables,
|
|
199
|
+
expressions: serialized.expressions,
|
|
200
|
+
components: serialized.components
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const textNodeIndex = node.children.findIndex((child) => child.type === "JSXText" && child.value.trim().length > 0);
|
|
205
|
+
if (textNodeIndex === -1) return null;
|
|
206
|
+
if (!node.children.every((child) => child.type === "JSXText" || child.type === "JSXElement" && isVoidElement(child))) return null;
|
|
207
|
+
const textNode = node.children[textNodeIndex];
|
|
208
|
+
const text = normalizeWhitespace(textNode.value);
|
|
209
|
+
if (text.length === 0) return null;
|
|
210
|
+
return {
|
|
211
|
+
kind: "text",
|
|
212
|
+
text,
|
|
213
|
+
textNodeIndex
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function registerEntry(entry, state, componentName) {
|
|
217
|
+
state.newEntries.push(entry);
|
|
218
|
+
const hashes = state.componentHashes.get(componentName) ?? [];
|
|
219
|
+
hashes.push(entry.hash);
|
|
220
|
+
state.componentHashes.set(componentName, hashes);
|
|
221
|
+
}
|
|
222
|
+
function rewriteChildren(path, state, translationScope, entryHash) {
|
|
223
|
+
if (translationScope.kind === "mixed") {
|
|
224
|
+
path.node.children = [constructTranslationCall(entryHash, translationScope.text, translationScope.args)];
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const { textNodeIndex, text } = translationScope;
|
|
228
|
+
path.node.children = path.node.children.map((child, index) => {
|
|
229
|
+
if (index === textNodeIndex) return constructTranslationCall(entryHash, text);
|
|
230
|
+
if (child.type === "JSXElement") transformVoidElement(child, state);
|
|
231
|
+
return child;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function processJSXElement(path, state) {
|
|
235
|
+
const component = state.componentsStack.at(-1);
|
|
236
|
+
if (!component) return;
|
|
237
|
+
const scope = getTranslationScope(path.node, state);
|
|
238
|
+
if (!scope) return;
|
|
239
|
+
const overrides = processOverrideAttributes(path);
|
|
240
|
+
const entry = createTranslationEntry("content", scope.text, { componentName: component.name }, state.filePath, path.node.loc?.start.line, path.node.loc?.start.column, overrides);
|
|
241
|
+
registerEntry(entry, state, component.name);
|
|
242
|
+
rewriteChildren(path, state, scope, entry.hash);
|
|
243
|
+
path.skip();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Inject dynamic locale attribute into <html> elements
|
|
247
|
+
* Transforms: <html> → <html lang={locale}>
|
|
248
|
+
*
|
|
249
|
+
* Only injects if:
|
|
250
|
+
* 1. Element is <html>
|
|
251
|
+
* 2. No existing lang/language attribute
|
|
252
|
+
* 3. We're inside a React component (has locale context)
|
|
253
|
+
*/
|
|
254
|
+
function injectHtmlLangAttribute(node, state) {
|
|
255
|
+
const openingElement = node.openingElement;
|
|
256
|
+
if (openingElement.name.type !== "JSXIdentifier" || openingElement.name.name !== "html") return;
|
|
257
|
+
if (openingElement.attributes.some((attr) => {
|
|
258
|
+
return attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && (attr.name.name === "lang" || attr.name.name === "language");
|
|
259
|
+
})) return;
|
|
260
|
+
const component = state.componentsStack.at(-1);
|
|
261
|
+
if (!component) return;
|
|
262
|
+
const langAttr = t.jsxAttribute(t.jsxIdentifier("lang"), t.jsxExpressionContainer(t.identifier("locale")));
|
|
263
|
+
openingElement.attributes.push(langAttr);
|
|
264
|
+
logger.debug(`Injected locale attribute into <html> element in component: ${component.name}`);
|
|
265
|
+
state.componentsNeedingLocale.add(component.name);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Inject translation hook into a component when it's done processing
|
|
269
|
+
* Called when popping component from stack
|
|
270
|
+
*/
|
|
271
|
+
function injectTranslationHook(path, component, state) {
|
|
272
|
+
const needsLocale = state.componentsNeedingLocale.has(component.name);
|
|
273
|
+
const hashes = state.componentHashes.get(component.name);
|
|
274
|
+
if ((!hashes || hashes.length === 0) && !needsLocale) return;
|
|
275
|
+
if (component.isAsync) {
|
|
276
|
+
injectServerHook(path, hashes ?? [], needsLocale);
|
|
277
|
+
state.needsAsyncImport = true;
|
|
278
|
+
} else {
|
|
279
|
+
injectUnifiedHook(path, hashes ?? [], needsLocale);
|
|
280
|
+
state.needsUnifiedImport = true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Add translation imports at the top of the file
|
|
285
|
+
* Called on Program exit after all components have been processed
|
|
286
|
+
*/
|
|
287
|
+
function addTranslationImports(programPath, state) {
|
|
288
|
+
if (state.needsAsyncImport) programPath.node.body.unshift(constructServerImport());
|
|
289
|
+
if (state.needsUnifiedImport) programPath.node.body.unshift(comstructUnifiedImport());
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Handle component function detection and state update
|
|
293
|
+
*/
|
|
294
|
+
function processComponentFunction(path, state) {
|
|
295
|
+
if (!isReactComponent(path)) {
|
|
296
|
+
path.skip();
|
|
297
|
+
logger.debug(`Skipping non-React component: ${path.node.type}`);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const componentName = inferComponentName(path);
|
|
301
|
+
if (!componentName) {
|
|
302
|
+
path.skip();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (!state.componentHashes.has(componentName)) state.componentHashes.set(componentName, []);
|
|
306
|
+
const componentEntry = {
|
|
307
|
+
name: componentName,
|
|
308
|
+
isAsync: path.node.async
|
|
309
|
+
};
|
|
310
|
+
state.componentsStack.push(componentEntry);
|
|
311
|
+
logger.debug(`Component ${JSON.stringify(componentEntry)} entered`);
|
|
312
|
+
path.setData("componentName", componentName);
|
|
313
|
+
path.traverse(componentVisitors, { visitorState: state });
|
|
314
|
+
const component = state.componentsStack.pop();
|
|
315
|
+
if (component) injectTranslationHook(path, component, state);
|
|
316
|
+
path.skip();
|
|
317
|
+
}
|
|
318
|
+
const componentVisitors = {
|
|
319
|
+
FunctionDeclaration(path) {
|
|
320
|
+
processComponentFunction(path, this.visitorState);
|
|
321
|
+
},
|
|
322
|
+
ArrowFunctionExpression(path) {
|
|
323
|
+
processComponentFunction(path, this.visitorState);
|
|
324
|
+
},
|
|
325
|
+
FunctionExpression(path) {
|
|
326
|
+
processComponentFunction(path, this.visitorState);
|
|
327
|
+
},
|
|
328
|
+
JSXFragment(path) {
|
|
329
|
+
processJSXElement(path, this.visitorState);
|
|
330
|
+
},
|
|
331
|
+
JSXElement(path) {
|
|
332
|
+
translateAttributes(path.node, this.visitorState);
|
|
333
|
+
injectHtmlLangAttribute(path.node, this.visitorState);
|
|
334
|
+
if (shouldSkipTranslationForElement(path.node)) {
|
|
335
|
+
path.skip();
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
processJSXElement(path, this.visitorState);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
const fileVisitors = {
|
|
342
|
+
ExportNamedDeclaration(path) {
|
|
343
|
+
const metadataState = processNextStaticMetadata(path, { filePath: this.visitorState.filePath });
|
|
344
|
+
if (metadataState) updateWithMetadataState(this.visitorState, metadataState);
|
|
345
|
+
},
|
|
346
|
+
FunctionDeclaration(path) {
|
|
347
|
+
const metadataState = processNextDynamicMetadata(path, { filePath: this.visitorState.filePath });
|
|
348
|
+
if (metadataState) updateWithMetadataState(this.visitorState, metadataState);
|
|
349
|
+
processComponentFunction(path, this.visitorState);
|
|
350
|
+
},
|
|
351
|
+
ArrowFunctionExpression(path) {
|
|
352
|
+
processComponentFunction(path, this.visitorState);
|
|
353
|
+
},
|
|
354
|
+
FunctionExpression(path) {
|
|
355
|
+
processComponentFunction(path, this.visitorState);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* Create Babel visitors for auto-translation (single-pass transformation)
|
|
360
|
+
*/
|
|
361
|
+
const programVisitor = { Program: {
|
|
362
|
+
enter(path) {
|
|
363
|
+
if (this.visitorState.needsDirective && !hasUseI18nDirective(path)) {
|
|
364
|
+
path.skip();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
path.traverse(fileVisitors, { visitorState: this.visitorState });
|
|
368
|
+
},
|
|
369
|
+
exit(path) {
|
|
370
|
+
if (this.visitorState.needsUnifiedImport || this.visitorState.needsAsyncImport) addTranslationImports(path, this.visitorState);
|
|
371
|
+
}
|
|
372
|
+
} };
|
|
373
|
+
function processFile(ast, options) {
|
|
374
|
+
const state = {
|
|
375
|
+
needsDirective: options.needsDirective,
|
|
376
|
+
filePath: options.relativeFilePath,
|
|
377
|
+
newEntries: [],
|
|
378
|
+
componentsStack: [],
|
|
379
|
+
componentHashes: /* @__PURE__ */ new Map(),
|
|
380
|
+
needsUnifiedImport: false,
|
|
381
|
+
needsAsyncImport: false,
|
|
382
|
+
componentsNeedingLocale: /* @__PURE__ */ new Set()
|
|
383
|
+
};
|
|
384
|
+
traverse(ast, programVisitor, void 0, { visitorState: state });
|
|
385
|
+
return state.newEntries;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
//#endregion
|
|
389
|
+
export { processFile };
|
|
390
|
+
//# sourceMappingURL=process-file.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-file.mjs","names":["variables: string[]","text","componentEntry: ComponentEntry","state: VisitorsInternalState"],"sources":["../../../src/plugin/transform/process-file.ts"],"sourcesContent":["/**\n * Keep mutations to the current node and children. Do not mutate program inserting imports when processing the component.\n * This should be done in the end.\n * The overall idea is that state is used for side effect tracking. When we exit relevant points, we check if we need to make extra changes at this level.\n * e.g. when we transform elements to insert translations, we record their hashes and add the translation hook call when leaving the component.\n */\nimport * as t from \"@babel/types\";\nimport type { NodePath, TraverseOptions } from \"@babel/traverse\";\nimport type { TranslationEntry } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport {\n comstructUnifiedImport,\n constructServerImport,\n constructTranslationCall,\n createTranslationEntry,\n escapeTextForICU,\n hasUseI18nDirective,\n inferComponentName,\n injectServerHook,\n injectUnifiedHook,\n isReactComponent,\n isVoidElement,\n normalizeWhitespace,\n} from \"./utils\";\nimport {\n type MetadataOutputState,\n processNextDynamicMetadata,\n processNextStaticMetadata,\n} from \"./metadata\";\nimport { traverse } from \"./babel-compat\";\nimport { processOverrideAttributes } from \"./parse-override\";\n\ntype ComponentEntry = {\n name: string;\n isAsync: boolean;\n};\n\n/**\n * State shared between all the visitors.\n */\nexport interface VisitorsInternalState {\n needsDirective: boolean;\n filePath: string;\n newEntries: TranslationEntry[];\n /** Stores component encountered up the tree at the current point, to avoid lookups. */\n componentsStack: ComponentEntry[];\n /** Map component names to their translation hashes */\n componentHashes: Map<string, string[]>;\n /** Track metadata functions that need translation */\n /** Track if we need the unified hook import */\n needsUnifiedImport: boolean;\n /** Track if we need the async API import */\n needsAsyncImport: boolean;\n /** Track which components need locale from hook (for <html> lang attribute) */\n componentsNeedingLocale: Set<string>;\n}\n\nfunction updateWithMetadataState(\n state: VisitorsInternalState,\n newState: MetadataOutputState,\n): VisitorsInternalState {\n state.newEntries = [...state.newEntries, ...newState.newEntries];\n state.needsAsyncImport = state.needsAsyncImport || newState.needsAsyncImport;\n return state;\n}\n\nconst SKIP_ATTRIBUTE = \"data-lingo-skip\";\n\n/**\n * Elements whose content should not be translated by default.\n * These are typically code-related or technical elements where translation\n * would break functionality or meaning.\n */\nconst NON_TRANSLATABLE_ELEMENTS = new Set([\n \"code\",\n \"pre\",\n \"script\",\n \"style\",\n \"kbd\",\n \"samp\",\n \"var\",\n]);\n\nconst TRANSLATABLE_ATTRIBUTES = new Set([\n \"title\",\n \"aria-label\",\n \"aria-description\",\n \"alt\",\n \"label\",\n \"description\",\n \"placeholder\",\n \"content\",\n \"subtitle\",\n]);\n\n/**\n * Check if a JSX element should skip translation based on:\n * 1. Element type (code, pre, script, style, etc.)\n * 2. translate=\"no\" attribute (HTML standard)\n * 3. data-lingo-skip attribute\n */\nfunction shouldSkipTranslationForElement(\n element: t.JSXElement | t.JSXFragment,\n): boolean {\n // We always translate fragments\n if (element.type === \"JSXFragment\") {\n return false;\n }\n const openingElement = element.openingElement;\n\n // Check element type\n if (openingElement.name.type === \"JSXIdentifier\") {\n if (NON_TRANSLATABLE_ELEMENTS.has(openingElement.name.name)) {\n return true;\n }\n }\n\n for (const attr of openingElement.attributes) {\n if (attr.type === \"JSXAttribute\" && attr.name.type === \"JSXIdentifier\") {\n // Check for translate=\"no\" (HTML standard)\n if (attr.name.name === \"translate\") {\n if (attr.value?.type === \"StringLiteral\" && attr.value.value === \"no\") {\n return true;\n }\n }\n\n if (attr.name.name === SKIP_ATTRIBUTE) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n// Called even on the element which are not translatable.\nfunction translateAttributes(\n node: t.JSXElement,\n state: VisitorsInternalState,\n): void {\n const openingElement = node.openingElement;\n\n const component = state.componentsStack.at(-1);\n if (!component) return;\n\n for (const attr of openingElement.attributes) {\n if (\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n TRANSLATABLE_ATTRIBUTES.has(attr.name.name) &&\n attr.value?.type === \"StringLiteral\"\n ) {\n const text = normalizeWhitespace(attr.value.value);\n if (text.length == 0) continue;\n\n const entry = createTranslationEntry(\n \"attribute\",\n text,\n {\n attributeName: attr.name.name,\n componentName: component.name,\n },\n state.filePath,\n node.loc?.start.line,\n node.loc?.start.column,\n );\n registerEntry(entry, state, component.name);\n\n attr.value = constructTranslationCall(entry.hash, text);\n }\n }\n}\n\n/**\n * Check if a JSX element has mixed content that needs rich text translation\n * Mixed content = text nodes + expressions + nested JSX elements (excluding void elements)\n */\nfunction hasMixedContent(element: t.JSXElement | t.JSXFragment): boolean {\n const children = element.children;\n if (children.length === 0) return false;\n // 001\n // 221\n // 101\n // States:\n // 0 - no text or translatable nodes\n // 1 - text node\n // 2 - expression or translatable element\n // 3 - interrupted text node\n // 4 - mixed\n //\n let state = 0;\n\n for (const child of children) {\n if (child.type === \"JSXText\") {\n if (child.value.trim().length > 0) {\n switch (state) {\n case 0:\n state = 1;\n break;\n case 1:\n break;\n case 2:\n return true;\n case 3:\n return true;\n }\n }\n // Expressions require a substitution of the placeholder and are usually a part of the text so should be translated in one context\n } else if (child.type === \"JSXExpressionContainer\") {\n switch (state) {\n case 0:\n state = 2;\n break;\n case 1:\n return true;\n case 2:\n break;\n case 3:\n return true;\n }\n } else if (child.type === \"JSXElement\") {\n if (!isVoidElement(child)) {\n switch (state) {\n case 0:\n state = 2;\n break;\n case 1:\n return true;\n case 2:\n break;\n case 3:\n return true;\n }\n } else {\n switch (state) {\n case 0:\n break;\n case 1:\n state = 3;\n break;\n case 2:\n break;\n case 3:\n break;\n }\n }\n }\n }\n return false;\n}\n\n/**\n * Result of serializing JSX children to a translation string\n */\ninterface SerializedJSX {\n /** The serialized string with placeholders, e.g. \"Hello {name}, you have <strong0>{count}</strong0> messages\" */\n text: string;\n /** Variable identifiers extracted from expressions, e.g. [\"name\", \"count\"] */\n variables: string[];\n expressions: Map<string, t.Expression>;\n /** Component mappings for nested elements, e.g. { strong0: <strong>...</strong> } */\n components: Map<string, t.JSXElement>;\n}\n\n/**\n * Serialize JSX children to a translation string with placeholders\n */\nfunction serializeJSXChildren(\n children: Array<\n | t.JSXText\n | t.JSXExpressionContainer\n | t.JSXElement\n | t.JSXSpreadChild\n | t.JSXFragment\n >,\n state: VisitorsInternalState,\n): SerializedJSX {\n let text = \"\";\n const variables: string[] = [];\n const expressions = new Map<string, t.Expression>();\n const components = new Map<string, t.JSXElement>();\n const elementCounts = new Map<string, number>();\n\n for (const child of children) {\n if (child.type === \"JSXText\") {\n // Normalize whitespace within this text node\n let normalized = normalizeWhitespace(child.value);\n\n // If we have accumulated text and this node starts with whitespace,\n // ensure there's a space separator\n if (\n text.length > 0 &&\n child.value.match(/^\\s/) &&\n normalized.length > 0\n ) {\n if (!text.endsWith(\" \")) {\n normalized = ` ${normalized}`;\n }\n }\n\n // If this node ends with whitespace and has content, add trailing space\n if (normalized.length > 0 && child.value.match(/\\s$/)) {\n if (!text.endsWith(\" \")) {\n normalized = `${normalized} `;\n }\n }\n\n text += escapeTextForICU(normalized);\n } else if (child.type === \"JSXExpressionContainer\") {\n // Extract variable name from expression\n const expr = child.expression;\n if (expr.type === \"Identifier\") {\n text += `{${expr.name}}`;\n if (!variables.includes(expr.name)) {\n variables.push(expr.name);\n }\n } else if (expr.type === \"StringLiteral\") {\n // String literal (like {\" \"}) - include the literal text directly\n text += escapeTextForICU(expr.value);\n } else if (expr.type !== \"JSXEmptyExpression\") {\n const name = `expression${expressions.size}`;\n text += `{${name}}`;\n expressions.set(name, expr);\n }\n } else if (child.type === \"JSXElement\") {\n let shouldTranslate = true;\n // Skip void elements (they cannot have children and shouldn't be in rich text)\n if (isVoidElement(child)) {\n // Void elements are ignored in the translation string\n // They will remain in their original position in the JSX tree\n shouldTranslate = false;\n }\n\n // Skip elements that should not be translated (code, pre, etc.)\n // These elements remain in the JSX tree but are not included in translation\n if (shouldSkipTranslationForElement(child)) {\n shouldTranslate = false;\n }\n\n // Get element name\n const openingElement = child.openingElement;\n let elementName = \"\";\n if (openingElement.name.type === \"JSXIdentifier\") {\n elementName = openingElement.name.name;\n } else {\n // TODO (AleksandrSl 02/12/2025): What is this corner case?\n // I thought it may be a fragment, but it seems that fragment is a separate JSXFragment node.\n elementName = \"element\";\n }\n\n // Generate unique tag name with index\n const count = elementCounts.get(elementName) || 0;\n elementCounts.set(elementName, count + 1);\n const tagName = `${elementName}${count}`;\n\n translateAttributes(child, state);\n\n if (shouldTranslate) {\n // Recursively serialize nested content\n const nested = serializeJSXChildren(child.children, state);\n text += `<${tagName}>${nested.text}</${tagName}>`;\n\n // Merge nested variables\n for (const v of nested.variables) {\n if (!variables.includes(v)) {\n variables.push(v);\n }\n }\n\n // Merge nested component mappings with prefixes to avoid conflicts\n for (const [nestedTag, nestedElement] of nested.components) {\n components.set(`${tagName}_${nestedTag}`, nestedElement);\n }\n } else {\n // There is no support for self-closing tags in format js, so we should generate empty ones.\n text += `<${tagName}></${tagName}>`;\n child.extra = {\n ...child.extra,\n shouldTranslate: false,\n };\n }\n\n // Store component mapping\n components.set(tagName, child);\n }\n }\n\n return { text, variables, expressions, components };\n}\n\nfunction transformVoidElement(\n node: t.JSXElement,\n state: VisitorsInternalState,\n) {\n translateAttributes(node, state);\n}\n\ntype TranslationScope =\n | { kind: \"mixed\"; text: string; args: any }\n | { kind: \"text\"; text: string; textNodeIndex: number };\n\nfunction getTranslationScope(\n node: t.JSXElement | t.JSXFragment,\n state: VisitorsInternalState,\n): TranslationScope | null {\n if (hasMixedContent(node)) {\n const serialized = serializeJSXChildren(node.children, state);\n const text = serialized.text.trim();\n if (text.length === 0) return null;\n\n return {\n kind: \"mixed\",\n text,\n args: {\n variables: serialized.variables,\n expressions: serialized.expressions,\n components: serialized.components,\n },\n };\n }\n\n // Non-mixed: allow exactly one meaningful JSXText + optional void elements + whitespace\n const textNodeIndex = node.children.findIndex(\n (child) => child.type === \"JSXText\" && child.value.trim().length > 0,\n );\n if (textNodeIndex === -1) return null;\n\n const allowed = node.children.every(\n (child) =>\n child.type === \"JSXText\" ||\n (child.type === \"JSXElement\" && isVoidElement(child)),\n );\n if (!allowed) return null;\n\n const textNode = node.children[textNodeIndex] as t.JSXText;\n const text = normalizeWhitespace(textNode.value);\n if (text.length === 0) return null;\n\n return { kind: \"text\", text, textNodeIndex };\n}\n\nfunction registerEntry(\n entry: ReturnType<typeof createTranslationEntry>,\n state: VisitorsInternalState,\n componentName: string,\n) {\n state.newEntries.push(entry);\n\n const hashes = state.componentHashes.get(componentName) ?? [];\n hashes.push(entry.hash);\n state.componentHashes.set(componentName, hashes);\n}\n\nfunction rewriteChildren(\n path: NodePath<t.JSXElement | t.JSXFragment>,\n state: VisitorsInternalState,\n translationScope: TranslationScope,\n entryHash: string,\n) {\n if (translationScope.kind === \"mixed\") {\n path.node.children = [\n constructTranslationCall(\n entryHash,\n translationScope.text,\n translationScope.args,\n ),\n ];\n return;\n }\n\n const { textNodeIndex, text } = translationScope;\n\n path.node.children = path.node.children.map((child, index) => {\n if (index === textNodeIndex) {\n return constructTranslationCall(entryHash, text);\n }\n if (child.type === \"JSXElement\") {\n transformVoidElement(child, state);\n }\n return child;\n });\n}\n\nfunction processJSXElement(\n path: NodePath<t.JSXElement | t.JSXFragment>,\n state: VisitorsInternalState,\n): void {\n const component = state.componentsStack.at(-1);\n if (!component) return;\n\n const scope = getTranslationScope(path.node, state);\n if (!scope) return;\n\n const overrides = processOverrideAttributes(path);\n\n const entry = createTranslationEntry(\n \"content\",\n scope.text,\n { componentName: component.name },\n state.filePath,\n path.node.loc?.start.line,\n path.node.loc?.start.column,\n overrides,\n );\n\n registerEntry(entry, state, component.name);\n rewriteChildren(path, state, scope, entry.hash);\n\n path.skip();\n}\n\n/**\n * Inject dynamic locale attribute into <html> elements\n * Transforms: <html> → <html lang={locale}>\n *\n * Only injects if:\n * 1. Element is <html>\n * 2. No existing lang/language attribute\n * 3. We're inside a React component (has locale context)\n */\nfunction injectHtmlLangAttribute(\n node: t.JSXElement,\n state: VisitorsInternalState,\n): void {\n const openingElement = node.openingElement;\n\n if (\n openingElement.name.type !== \"JSXIdentifier\" ||\n openingElement.name.name !== \"html\"\n ) {\n return;\n }\n\n // Check if lang attribute already exists\n const hasLangAttr = openingElement.attributes.some((attr) => {\n return (\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n (attr.name.name === \"lang\" || attr.name.name === \"language\")\n );\n });\n\n if (hasLangAttr) {\n // Already has lang attribute, don't inject\n return;\n }\n\n // Check if we're inside a component (has access to locale context)\n const component = state.componentsStack.at(-1);\n if (!component) {\n // Not inside a component, can't inject locale\n return;\n }\n\n // Inject lang={locale} attribute\n // This creates: <html lang={locale}>\n const langAttr = t.jsxAttribute(\n t.jsxIdentifier(\"lang\"),\n t.jsxExpressionContainer(t.identifier(\"locale\")),\n );\n\n openingElement.attributes.push(langAttr);\n\n logger.debug(\n `Injected locale attribute into <html> element in component: ${component.name}`,\n );\n\n // Mark that this component needs locale destructured from the hook\n state.componentsNeedingLocale.add(component.name);\n}\n\n/**\n * Inject translation hook into a component when it's done processing\n * Called when popping component from stack\n */\nexport function injectTranslationHook(\n path: NodePath<\n t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression\n >,\n component: ComponentEntry,\n state: VisitorsInternalState,\n): void {\n const needsLocale = state.componentsNeedingLocale.has(component.name);\n const hashes = state.componentHashes.get(component.name);\n if ((!hashes || hashes.length === 0) && !needsLocale) {\n return; // No translations needed\n }\n\n if (component.isAsync) {\n // Async component uses getServerTranslations\n injectServerHook(path, hashes ?? [], needsLocale);\n state.needsAsyncImport = true;\n } else {\n // Non-async component uses unified hook (useTranslation)\n injectUnifiedHook(path, hashes ?? [], needsLocale);\n state.needsUnifiedImport = true;\n }\n}\n\n/**\n * Add translation imports at the top of the file\n * Called on Program exit after all components have been processed\n */\nfunction addTranslationImports(\n programPath: NodePath<t.Program>,\n state: VisitorsInternalState,\n): void {\n // A file can have both async and non-async components!\n if (state.needsAsyncImport) {\n programPath.node.body.unshift(constructServerImport());\n }\n if (state.needsUnifiedImport) {\n programPath.node.body.unshift(comstructUnifiedImport());\n }\n}\n\n/**\n * Handle component function detection and state update\n */\nfunction processComponentFunction(\n path: NodePath<\n t.FunctionDeclaration | t.FunctionExpression | t.ArrowFunctionExpression\n >,\n state: VisitorsInternalState,\n): void {\n if (!isReactComponent(path)) {\n path.skip();\n logger.debug(`Skipping non-React component: ${path.node.type}`);\n return;\n }\n\n const componentName = inferComponentName(path);\n if (!componentName) {\n path.skip();\n return;\n }\n\n // Store component info for later hook injection\n if (!state.componentHashes.has(componentName)) {\n state.componentHashes.set(componentName, []);\n }\n\n // Push component to stack with async info and path reference\n const componentEntry: ComponentEntry = {\n name: componentName,\n isAsync: path.node.async,\n };\n state.componentsStack.push(componentEntry);\n logger.debug(`Component ${JSON.stringify(componentEntry)} entered`);\n\n path.setData(\"componentName\", componentName);\n path.traverse(componentVisitors, { visitorState: state });\n const component = state.componentsStack.pop();\n if (component) {\n injectTranslationHook(path, component, state);\n }\n // We have already traversed the path's children so the main traverse can skip it.\n path.skip();\n}\n\nconst componentVisitors = {\n // 1. Handle nested components. FunctionDeclaration|FunctionExpression|ArrowFunctionExpression unfortunately doesn't give the correct type signature for the path.\n FunctionDeclaration(path: NodePath<t.FunctionDeclaration>) {\n processComponentFunction(path, this.visitorState);\n },\n ArrowFunctionExpression(path: NodePath<t.ArrowFunctionExpression>) {\n processComponentFunction(path, this.visitorState);\n },\n FunctionExpression(path: NodePath<t.FunctionExpression>) {\n processComponentFunction(path, this.visitorState);\n },\n // 2. Fragments are separate from other JSX elements. Both <> and <Fragment> are the same AST node type.\n JSXFragment(path: NodePath<t.JSXFragment>) {\n // No attributes to translate on the fragment, so we only check if it has mixed content. If it doesn't, go ahead and its children will be checked.\n // We also do not check for translation skip, because fragments are mostly used to make the bare text translatable.\n // But if we want to support <Fragment data-lingo-skip>Text</Fragment> we should do it here.\n processJSXElement(path, this.visitorState);\n },\n\n // Transform JSX elements with mixed content (text + expressions or nested elements)\n JSXElement(path: NodePath<t.JSXElement>) {\n translateAttributes(path.node, this.visitorState);\n\n // Inject locale attribute into <html> elements for Next.js\n injectHtmlLangAttribute(path.node, this.visitorState);\n\n if (shouldSkipTranslationForElement(path.node)) {\n path.skip();\n return;\n }\n processJSXElement(path, this.visitorState);\n },\n} satisfies TraverseOptions<{ visitorState: VisitorsInternalState }>;\n\nconst fileVisitors = {\n // Handle static metadata exports: export const metadata = { ... }\n ExportNamedDeclaration(path: NodePath<t.ExportNamedDeclaration>) {\n // Check if the export is a metadata export and converts it to a function\n const metadataState = processNextStaticMetadata(path, {\n filePath: this.visitorState.filePath,\n });\n\n if (metadataState) {\n updateWithMetadataState(this.visitorState, metadataState);\n }\n },\n\n // All component function types share the same handler\n FunctionDeclaration(path: NodePath<t.FunctionDeclaration>) {\n const metadataState = processNextDynamicMetadata(path, {\n filePath: this.visitorState.filePath,\n });\n if (metadataState) {\n updateWithMetadataState(this.visitorState, metadataState);\n }\n\n // Otherwise, handle as component\n processComponentFunction(path, this.visitorState);\n },\n\n // export const MyComponent = () => {}\n ArrowFunctionExpression(path: NodePath<t.ArrowFunctionExpression>) {\n processComponentFunction(path, this.visitorState);\n },\n\n // All these are considered function expressions\n // const myFunction = function() {\n // return 'hello';\n // };\n //\n // const myFunction2 = function namedFunc() {\n // return 'hello';\n // };\n //\n // setTimeout(function() {\n // console.log('hello');\n // }, 1000);\n // While\n // export default function() {}\n // is not\n FunctionExpression(path: NodePath<t.FunctionExpression>) {\n processComponentFunction(path, this.visitorState);\n },\n} satisfies TraverseOptions<{ visitorState: VisitorsInternalState }>;\n\n/**\n * Create Babel visitors for auto-translation (single-pass transformation)\n */\nconst programVisitor = {\n Program: {\n enter(path: NodePath<t.Program>) {\n // We do the check on the loader level, but since they could be false positive, accepting the files we don't need to process, we do the check here as well.\n if (this.visitorState.needsDirective && !hasUseI18nDirective(path)) {\n path.skip();\n return;\n }\n\n path.traverse(fileVisitors, {\n visitorState: this.visitorState,\n });\n },\n\n exit(path: NodePath<t.Program>) {\n if (\n this.visitorState.needsUnifiedImport ||\n this.visitorState.needsAsyncImport\n ) {\n addTranslationImports(path, this.visitorState);\n }\n },\n },\n} satisfies TraverseOptions<{ visitorState: VisitorsInternalState }>;\n\nexport function processFile(\n ast: t.Node,\n options: { relativeFilePath: string; needsDirective: boolean },\n) {\n const state: VisitorsInternalState = {\n needsDirective: options.needsDirective,\n filePath: options.relativeFilePath,\n newEntries: [],\n componentsStack: [],\n componentHashes: new Map<string, string[]>(),\n needsUnifiedImport: false,\n needsAsyncImport: false,\n componentsNeedingLocale: new Set<string>(),\n };\n\n traverse(ast, programVisitor, undefined, { visitorState: state });\n\n return state.newEntries;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-i18n.mjs","names":[],"sources":["../../../src/plugin/transform/use-i18n.ts"],"sourcesContent":["export const useI18n = \"use i18n\";\nexport const useI18nRegex = new RegExp(`[\"']${useI18n}[\"']`);\n"],"mappings":";AAAA,MAAa,UAAU;AACvB,MAAa,+BAAe,IAAI,OAAO,OAAO,QAAQ,MAAM"}
|