@intlayer/engine 9.5.9-canary.0 → 9.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/_virtual/_utils_asset.cjs +1 -1
- package/dist/cjs/build.cjs +4 -4
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +2 -2
- package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs +3 -3
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeNestedDictionary.cjs +3 -3
- package/dist/cjs/buildIntlayerDictionary/writeRemoteDictionary.cjs +2 -2
- package/dist/cjs/buildIntlayerDictionary/writeUnmergedDictionary.cjs +2 -2
- package/dist/cjs/cleanOutputDir.cjs +1 -1
- package/dist/cjs/cleanRemovedContentDeclaration.cjs +1 -1
- package/dist/cjs/cli.cjs +10 -3
- package/dist/cjs/createDictionaryEntryPoint/createDictionaryEntryPoint.cjs +1 -1
- package/dist/cjs/createDictionaryEntryPoint/getBuiltDictionariesPath.cjs +2 -2
- package/dist/cjs/createDictionaryEntryPoint/getBuiltDynamicDictionariesPath.cjs +2 -2
- package/dist/cjs/createDictionaryEntryPoint/getBuiltFetchDictionariesPath.cjs +2 -2
- package/dist/cjs/createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.cjs +2 -2
- package/dist/cjs/createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.cjs +2 -2
- package/dist/cjs/createType/createModuleAugmentation.cjs +3 -3
- package/dist/cjs/createType/createType.cjs +1 -1
- package/dist/cjs/docReview/index.cjs +5 -5
- package/dist/cjs/docReview/pipeline.cjs +4 -4
- package/dist/cjs/docReview/reviewReport.cjs.map +1 -1
- package/dist/cjs/fetchDistantDictionaries.cjs +1 -1
- package/dist/cjs/handleContentDeclarationFileChange.cjs +2 -2
- package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs +1 -1
- package/dist/cjs/init/cms.cjs +35 -0
- package/dist/cjs/init/cms.cjs.map +1 -1
- package/dist/cjs/init/documentationRouter.cjs +141 -0
- package/dist/cjs/init/documentationRouter.cjs.map +1 -0
- package/dist/cjs/init/frameworkSetup/backend/index.cjs +138 -0
- package/dist/cjs/init/frameworkSetup/backend/index.cjs.map +1 -0
- package/dist/cjs/init/frameworkSetup/backend/transforms.cjs +223 -0
- package/dist/cjs/init/frameworkSetup/backend/transforms.cjs.map +1 -0
- package/dist/cjs/init/frameworkSetup/index.cjs +9 -3
- package/dist/cjs/init/frameworkSetup/index.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/localeRouting.cjs +13 -0
- package/dist/cjs/init/frameworkSetup/localeRouting.cjs.map +1 -0
- package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs +34 -7
- package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs +56 -0
- package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +2 -2
- package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs +32 -3
- package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs +58 -0
- package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +94 -14
- package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
- package/dist/cjs/init/index.cjs +46 -159
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/configManipulation.cjs +39 -0
- package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
- package/dist/cjs/init/utils/fileSystem.cjs +1 -1
- package/dist/cjs/init/utils/githubActions.cjs +1 -1
- package/dist/cjs/init/utils/index.cjs +10 -0
- package/dist/cjs/init/utils/intlayerDependencies.cjs +1 -1
- package/dist/cjs/init/utils/lintConfig.cjs +52 -0
- package/dist/cjs/init/utils/lintConfig.cjs.map +1 -0
- package/dist/cjs/init/utils/packageManager.cjs +63 -14
- package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
- package/dist/cjs/init/utils/urlRoutingFramework.cjs +41 -0
- package/dist/cjs/init/utils/urlRoutingFramework.cjs.map +1 -0
- package/dist/cjs/initConfig/index.cjs +5 -2
- package/dist/cjs/initConfig/index.cjs.map +1 -1
- package/dist/cjs/installLSP.cjs +1 -1
- package/dist/cjs/installMCP/installMCP.cjs +1 -1
- package/dist/cjs/installSkills/index.cjs +1 -1
- package/dist/cjs/listDictionariesPath.cjs +1 -1
- package/dist/cjs/listGitFiles.cjs +1 -1
- package/dist/cjs/loadDictionaries/getIntlayerBundle.cjs +2 -2
- package/dist/cjs/loadDictionaries/index.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadContentDeclaration.cjs +2 -2
- package/dist/cjs/loadDictionaries/loadDictionaries.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadMarkdownContentDeclaration.cjs +1 -1
- package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs +104 -43
- package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs.map +1 -1
- package/dist/cjs/logConfigDetails.cjs +1 -1
- package/dist/cjs/prepareIntlayer.cjs +4 -4
- package/dist/cjs/scan/index.cjs +1 -1
- package/dist/cjs/scan/scanWebsite.cjs +1 -1
- package/dist/cjs/syncPluginKit/createFileAdapter.cjs +1 -1
- package/dist/cjs/syncPluginKit/index.cjs +1 -1
- package/dist/cjs/utils/buildComponentFilesList.cjs +1 -1
- package/dist/cjs/utils/contentWatcherLock.cjs +2 -2
- package/dist/cjs/utils/index.cjs +4 -4
- package/dist/cjs/utils/readDictionariesFromDisk.cjs +1 -1
- package/dist/cjs/utils/runOnce.cjs +1 -1
- package/dist/cjs/utils/runParallel/pidTree.cjs +1 -1
- package/dist/cjs/utils/withFileLock.cjs +1 -1
- package/dist/cjs/watcher.cjs +6 -6
- package/dist/cjs/writeConfiguration/index.cjs +1 -1
- package/dist/cjs/writeContentDeclaration/processContentDeclarationContent.cjs +2 -2
- package/dist/cjs/writeContentDeclaration/transformJSFile.cjs +1 -1
- package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs +4 -4
- package/dist/cjs/writeContentDeclaration/writeJSFile.cjs +3 -3
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +1 -1
- package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs +1 -1
- package/dist/cjs/writeFileIfChanged.cjs +1 -1
- package/dist/esm/_virtual/_utils_asset.mjs +1 -1
- package/dist/esm/build.mjs +4 -4
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +2 -2
- package/dist/esm/buildIntlayerDictionary/writeFetchDictionary.mjs +3 -3
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs +1 -1
- package/dist/esm/buildIntlayerDictionary/writeNestedDictionary.mjs +3 -3
- package/dist/esm/buildIntlayerDictionary/writeRemoteDictionary.mjs +2 -2
- package/dist/esm/buildIntlayerDictionary/writeUnmergedDictionary.mjs +2 -2
- package/dist/esm/cleanOutputDir.mjs +1 -1
- package/dist/esm/cleanRemovedContentDeclaration.mjs +1 -1
- package/dist/esm/cli.mjs +8 -6
- package/dist/esm/createDictionaryEntryPoint/createDictionaryEntryPoint.mjs +1 -1
- package/dist/esm/createDictionaryEntryPoint/getBuiltDictionariesPath.mjs +2 -2
- package/dist/esm/createDictionaryEntryPoint/getBuiltDynamicDictionariesPath.mjs +2 -2
- package/dist/esm/createDictionaryEntryPoint/getBuiltFetchDictionariesPath.mjs +2 -2
- package/dist/esm/createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.mjs +2 -2
- package/dist/esm/createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.mjs +2 -2
- package/dist/esm/createType/createModuleAugmentation.mjs +3 -3
- package/dist/esm/createType/createType.mjs +1 -1
- package/dist/esm/docReview/index.mjs +5 -5
- package/dist/esm/docReview/pipeline.mjs +4 -4
- package/dist/esm/docReview/reviewReport.mjs.map +1 -1
- package/dist/esm/fetchDistantDictionaries.mjs +1 -1
- package/dist/esm/handleContentDeclarationFileChange.mjs +2 -2
- package/dist/esm/handleUnlinkedContentDeclarationFile.mjs +1 -1
- package/dist/esm/init/cms.mjs +35 -2
- package/dist/esm/init/cms.mjs.map +1 -1
- package/dist/esm/init/documentationRouter.mjs +139 -0
- package/dist/esm/init/documentationRouter.mjs.map +1 -0
- package/dist/esm/init/frameworkSetup/backend/index.mjs +130 -0
- package/dist/esm/init/frameworkSetup/backend/index.mjs.map +1 -0
- package/dist/esm/init/frameworkSetup/backend/transforms.mjs +216 -0
- package/dist/esm/init/frameworkSetup/backend/transforms.mjs.map +1 -0
- package/dist/esm/init/frameworkSetup/index.mjs +9 -3
- package/dist/esm/init/frameworkSetup/index.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/localeRouting.mjs +12 -0
- package/dist/esm/init/frameworkSetup/localeRouting.mjs.map +1 -0
- package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs +36 -9
- package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/templates.mjs +54 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/templates.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +2 -2
- package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs +35 -6
- package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs +56 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +94 -15
- package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
- package/dist/esm/init/index.mjs +42 -158
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/configManipulation.mjs +38 -1
- package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
- package/dist/esm/init/utils/fileSystem.mjs +1 -1
- package/dist/esm/init/utils/githubActions.mjs +1 -1
- package/dist/esm/init/utils/index.mjs +5 -3
- package/dist/esm/init/utils/intlayerDependencies.mjs +1 -1
- package/dist/esm/init/utils/lintConfig.mjs +50 -0
- package/dist/esm/init/utils/lintConfig.mjs.map +1 -0
- package/dist/esm/init/utils/packageManager.mjs +61 -15
- package/dist/esm/init/utils/packageManager.mjs.map +1 -1
- package/dist/esm/init/utils/urlRoutingFramework.mjs +40 -0
- package/dist/esm/init/utils/urlRoutingFramework.mjs.map +1 -0
- package/dist/esm/initConfig/index.mjs +5 -2
- package/dist/esm/initConfig/index.mjs.map +1 -1
- package/dist/esm/installLSP.mjs +1 -1
- package/dist/esm/installMCP/installMCP.mjs +1 -1
- package/dist/esm/installSkills/index.mjs +1 -1
- package/dist/esm/listDictionariesPath.mjs +1 -1
- package/dist/esm/listGitFiles.mjs +1 -1
- package/dist/esm/loadDictionaries/getIntlayerBundle.mjs +2 -2
- package/dist/esm/loadDictionaries/index.mjs +1 -1
- package/dist/esm/loadDictionaries/loadContentDeclaration.mjs +2 -2
- package/dist/esm/loadDictionaries/loadDictionaries.mjs +1 -1
- package/dist/esm/loadDictionaries/loadLocalDictionaries.mjs +1 -1
- package/dist/esm/loadDictionaries/loadMarkdownContentDeclaration.mjs +1 -1
- package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs +105 -44
- package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs.map +1 -1
- package/dist/esm/logConfigDetails.mjs +1 -1
- package/dist/esm/prepareIntlayer.mjs +4 -4
- package/dist/esm/scan/index.mjs +1 -1
- package/dist/esm/scan/scanWebsite.mjs +1 -1
- package/dist/esm/syncPluginKit/createFileAdapter.mjs +1 -1
- package/dist/esm/syncPluginKit/index.mjs +1 -1
- package/dist/esm/utils/buildComponentFilesList.mjs +1 -1
- package/dist/esm/utils/contentWatcherLock.mjs +2 -2
- package/dist/esm/utils/index.mjs +4 -4
- package/dist/esm/utils/readDictionariesFromDisk.mjs +1 -1
- package/dist/esm/utils/runOnce.mjs +1 -1
- package/dist/esm/utils/runParallel/pidTree.mjs +1 -1
- package/dist/esm/utils/withFileLock.mjs +1 -1
- package/dist/esm/watcher.mjs +6 -6
- package/dist/esm/writeConfiguration/index.mjs +1 -1
- package/dist/esm/writeContentDeclaration/processContentDeclarationContent.mjs +2 -2
- package/dist/esm/writeContentDeclaration/transformJSFile.mjs +1 -1
- package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs +4 -4
- package/dist/esm/writeContentDeclaration/writeJSFile.mjs +3 -3
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +1 -1
- package/dist/esm/writeContentDeclaration/writeYamlFile.mjs +1 -1
- package/dist/esm/writeFileIfChanged.mjs +1 -1
- package/dist/types/cli.d.ts +5 -3
- package/dist/types/docReview/reviewReport.d.ts.map +1 -1
- package/dist/types/formatDictionary.d.ts +2 -2
- package/dist/types/init/cms.d.ts +11 -0
- package/dist/types/init/cms.d.ts.map +1 -1
- package/dist/types/init/documentationRouter.d.ts +64 -0
- package/dist/types/init/documentationRouter.d.ts.map +1 -0
- package/dist/types/init/frameworkSetup/backend/index.d.ts +16 -0
- package/dist/types/init/frameworkSetup/backend/index.d.ts.map +1 -0
- package/dist/types/init/frameworkSetup/backend/transforms.d.ts +37 -0
- package/dist/types/init/frameworkSetup/backend/transforms.d.ts.map +1 -0
- package/dist/types/init/frameworkSetup/index.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/localeRouting.d.ts +11 -0
- package/dist/types/init/frameworkSetup/localeRouting.d.ts.map +1 -0
- package/dist/types/init/frameworkSetup/nextAppRouter/index.d.ts +5 -5
- package/dist/types/init/frameworkSetup/nextAppRouter/index.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/nextAppRouter/templates.d.ts +12 -0
- package/dist/types/init/frameworkSetup/nextAppRouter/templates.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts +10 -1
- package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/tanstackStart/index.d.ts +3 -2
- package/dist/types/init/frameworkSetup/tanstackStart/index.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts +10 -0
- package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts +12 -0
- package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
- package/dist/types/init/frameworkSetup/types.d.ts +6 -0
- package/dist/types/init/frameworkSetup/types.d.ts.map +1 -1
- package/dist/types/init/index.d.ts +15 -1
- package/dist/types/init/index.d.ts.map +1 -1
- package/dist/types/init/utils/configManipulation.d.ts +14 -0
- package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
- package/dist/types/init/utils/index.d.ts +5 -3
- package/dist/types/init/utils/lintConfig.d.ts +28 -0
- package/dist/types/init/utils/lintConfig.d.ts.map +1 -0
- package/dist/types/init/utils/packageManager.d.ts +29 -6
- package/dist/types/init/utils/packageManager.d.ts.map +1 -1
- package/dist/types/init/utils/urlRoutingFramework.d.ts +9 -0
- package/dist/types/init/utils/urlRoutingFramework.d.ts.map +1 -0
- package/dist/types/initConfig/index.d.ts +3 -1
- package/dist/types/initConfig/index.d.ts.map +1 -1
- package/dist/types/loadDictionaries/logTypeScriptErrors.d.ts +4 -0
- package/dist/types/loadDictionaries/logTypeScriptErrors.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ensureDirectory, exists, readFileFromRoot, writeFileToRoot } from "../../utils/fileSystem.mjs";
|
|
2
|
+
import { usesLocalePathSegment } from "../localeRouting.mjs";
|
|
2
3
|
import { findAppFile } from "../nextAppRouter/detect.mjs";
|
|
3
4
|
import { detectTanStackRoutesDir, hasTanStackStartDeps } from "./detect.mjs";
|
|
4
|
-
import { getLocaleSegment, restructureRoutesIntoLocale } from "./restructure.mjs";
|
|
5
|
-
import { buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs } from "./templates.mjs";
|
|
6
|
-
import { wrapRootWithProvider } from "./transforms.mjs";
|
|
5
|
+
import { getLocaleSegment, isLocaleSegment, restructureRoutesIntoLocale } from "./restructure.mjs";
|
|
6
|
+
import { UNPREFIXED_ROOT_TEMPLATE_JS, UNPREFIXED_ROOT_TEMPLATE_TS, buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs } from "./templates.mjs";
|
|
7
|
+
import { wrapRootWithProvider, wrapRootWithRequestLocaleProvider } from "./transforms.mjs";
|
|
7
8
|
import { join } from "node:path";
|
|
8
9
|
import * as ANSIColors from "@intlayer/config/colors";
|
|
9
10
|
import { colorize, colorizePath, logger, v, x } from "@intlayer/config/logger";
|
|
11
|
+
import { readdir } from "node:fs/promises";
|
|
10
12
|
|
|
11
13
|
//#region src/init/frameworkSetup/tanstackStart/index.ts
|
|
12
14
|
/** Logs the outcome of the root-document provider-wrap transform. */
|
|
@@ -35,8 +37,28 @@ const createIfMissing = async (rootDir, filePath, content, description) => {
|
|
|
35
37
|
return true;
|
|
36
38
|
};
|
|
37
39
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* Wires routes whose URLs carry no locale segment (`no-prefix`,
|
|
41
|
+
* `search-params`, or no proxy): routes stay in place and the root document
|
|
42
|
+
* provides the locale resolved per request. Routes already nested under a
|
|
43
|
+
* locale segment are left untouched, with guidance.
|
|
44
|
+
*/
|
|
45
|
+
const setupUnprefixedRoutes = async (rootDir, routesDir, useTypeScript) => {
|
|
46
|
+
const existingLocaleSegment = (await readdir(join(rootDir, routesDir))).find(isLocaleSegment);
|
|
47
|
+
if (existingLocaleSegment) {
|
|
48
|
+
logger(`${x} Routes live under ${colorizePath(join(routesDir, existingLocaleSegment))} but the chosen routing has no locale in the URL — move them back to ${colorizePath(routesDir)} manually.`, { level: "warn" });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const existingRoot = await findAppFile(rootDir, routesDir, "__root");
|
|
52
|
+
if (existingRoot) {
|
|
53
|
+
await transformExistingFile(rootDir, existingRoot, wrapRootWithRequestLocaleProvider);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await createIfMissing(rootDir, join(routesDir, `__root.${useTypeScript ? "tsx" : "jsx"}`), useTypeScript ? UNPREFIXED_ROOT_TEMPLATE_TS : UNPREFIXED_ROOT_TEMPLATE_JS, "root document");
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* TanStack Start adapter. For prefix routing modes, restructures the file-based
|
|
60
|
+
* routes under a `{-$locale}` / `$locale` segment and scaffolds the locale
|
|
61
|
+
* segment route; for the others, keeps routes in place. Wraps the root document with
|
|
40
62
|
* the Intlayer provider — all idempotently and without overwriting user code it
|
|
41
63
|
* cannot confidently transform.
|
|
42
64
|
*/
|
|
@@ -46,13 +68,20 @@ const tanStackStartAdapter = {
|
|
|
46
68
|
if (!hasTanStackStartDeps(allDeps)) return false;
|
|
47
69
|
return await detectTanStackRoutesDir(rootDir) !== null;
|
|
48
70
|
},
|
|
49
|
-
setup: async ({ rootDir, useTypeScript, routingMode }) => {
|
|
71
|
+
setup: async ({ rootDir, useTypeScript, routingMode, enableProxy }) => {
|
|
50
72
|
const routesInfo = await detectTanStackRoutesDir(rootDir);
|
|
51
73
|
if (!routesInfo) return;
|
|
52
74
|
const { routesDir } = routesInfo;
|
|
53
75
|
const scriptExtension = useTypeScript ? "tsx" : "jsx";
|
|
54
76
|
const targetLocaleSegment = getLocaleSegment(routingMode);
|
|
55
77
|
logger(colorize("Setting up TanStack Start integration...", ANSIColors.CYAN));
|
|
78
|
+
if (!usesLocalePathSegment({
|
|
79
|
+
routingMode,
|
|
80
|
+
enableProxy
|
|
81
|
+
})) {
|
|
82
|
+
await setupUnprefixedRoutes(rootDir, routesDir, useTypeScript);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
56
85
|
const restructureResult = await restructureRoutesIntoLocale(rootDir, routesDir, targetLocaleSegment);
|
|
57
86
|
if (restructureResult.status === "moved") logger(`${v} Restructured routes under ${colorizePath(join(routesDir, targetLocaleSegment))}: ${restructureResult.movedEntries.map((entry) => colorize(entry, ANSIColors.MAGENTA)).join(", ")}`);
|
|
58
87
|
else if (restructureResult.status === "already-structured") logger(`${v} ${colorizePath(join(routesDir, restructureResult.localeSegment))} already exists, skipping restructure`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport {\n ensureDirectory,\n exists,\n readFileFromRoot,\n writeFileToRoot,\n} from '../../utils/fileSystem';\nimport { findAppFile } from '../nextAppRouter/detect';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectTanStackRoutesDir, hasTanStackStartDeps } from './detect';\nimport {
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/index.ts"],"sourcesContent":["import { readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport {\n ensureDirectory,\n exists,\n readFileFromRoot,\n writeFileToRoot,\n} from '../../utils/fileSystem';\nimport { usesLocalePathSegment } from '../localeRouting';\nimport { findAppFile } from '../nextAppRouter/detect';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectTanStackRoutesDir, hasTanStackStartDeps } from './detect';\nimport {\n getLocaleSegment,\n isLocaleSegment,\n restructureRoutesIntoLocale,\n} from './restructure';\nimport {\n buildLocaleRouteTemplate,\n buildRootTemplateJs,\n buildRootTemplateTs,\n UNPREFIXED_ROOT_TEMPLATE_JS,\n UNPREFIXED_ROOT_TEMPLATE_TS,\n} from './templates';\nimport {\n type TransformResult,\n wrapRootWithProvider,\n wrapRootWithRequestLocaleProvider,\n} from './transforms';\n\n/** Logs the outcome of the root-document provider-wrap transform. */\nconst logTransformOutcome = (\n filePath: string,\n result: TransformResult\n): void => {\n switch (result.status) {\n case 'wrapped':\n logger(`${v} Added Intlayer provider to ${colorizePath(filePath)}`);\n break;\n case 'already':\n logger(\n `${v} ${colorizePath(filePath)} already wraps the Intlayer provider`\n );\n break;\n case 'skipped':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wrap the document's {children} with <IntlayerProvider locale={locale}> manually.`,\n { level: 'warn' }\n );\n break;\n }\n};\n\n/** Transforms an existing file in place, writing it back only when changed. */\nconst transformExistingFile = async (\n rootDir: string,\n filePath: string,\n transform: (code: string) => TransformResult\n): Promise<void> => {\n const code = await readFileFromRoot(rootDir, filePath);\n const result = transform(code);\n if (result.status === 'wrapped') {\n await writeFileToRoot(rootDir, filePath, result.code);\n }\n logTransformOutcome(filePath, result);\n};\n\n/** Creates a file from a template only when it does not already exist. */\nconst createIfMissing = async (\n rootDir: string,\n filePath: string,\n content: string,\n description: string\n): Promise<boolean> => {\n if (await exists(rootDir, filePath)) return false;\n await writeFileToRoot(rootDir, filePath, content);\n logger(`${v} Created ${description} ${colorizePath(filePath)}`);\n return true;\n};\n\n/**\n * Wires routes whose URLs carry no locale segment (`no-prefix`,\n * `search-params`, or no proxy): routes stay in place and the root document\n * provides the locale resolved per request. Routes already nested under a\n * locale segment are left untouched, with guidance.\n */\nconst setupUnprefixedRoutes = async (\n rootDir: string,\n routesDir: string,\n useTypeScript: boolean\n): Promise<void> => {\n const entries = await readdir(join(rootDir, routesDir));\n const existingLocaleSegment = entries.find(isLocaleSegment);\n if (existingLocaleSegment) {\n logger(\n `${x} Routes live under ${colorizePath(join(routesDir, existingLocaleSegment))} but the chosen routing has no locale in the URL — move them back to ${colorizePath(routesDir)} manually.`,\n { level: 'warn' }\n );\n return;\n }\n\n const existingRoot = await findAppFile(rootDir, routesDir, '__root');\n if (existingRoot) {\n await transformExistingFile(\n rootDir,\n existingRoot,\n wrapRootWithRequestLocaleProvider\n );\n return;\n }\n\n await createIfMissing(\n rootDir,\n join(routesDir, `__root.${useTypeScript ? 'tsx' : 'jsx'}`),\n useTypeScript ? UNPREFIXED_ROOT_TEMPLATE_TS : UNPREFIXED_ROOT_TEMPLATE_JS,\n 'root document'\n );\n};\n\n/**\n * TanStack Start adapter. For prefix routing modes, restructures the file-based\n * routes under a `{-$locale}` / `$locale` segment and scaffolds the locale\n * segment route; for the others, keeps routes in place. Wraps the root document with\n * the Intlayer provider — all idempotently and without overwriting user code it\n * cannot confidently transform.\n */\nexport const tanStackStartAdapter: FrameworkAdapter = {\n name: 'TanStack Start',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!hasTanStackStartDeps(allDeps)) return false;\n return (await detectTanStackRoutesDir(rootDir)) !== null;\n },\n\n setup: async ({\n rootDir,\n useTypeScript,\n routingMode,\n enableProxy,\n }: FrameworkSetupContext) => {\n const routesInfo = await detectTanStackRoutesDir(rootDir);\n if (!routesInfo) return;\n\n const { routesDir } = routesInfo;\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n // `prefix-all` needs the required `$locale` segment; every other mode uses\n // the optional `{-$locale}` segment so the default locale stays prefix-free.\n const targetLocaleSegment = getLocaleSegment(routingMode);\n\n logger(\n colorize('Setting up TanStack Start integration...', ANSIColors.CYAN)\n );\n\n if (!usesLocalePathSegment({ routingMode, enableProxy })) {\n await setupUnprefixedRoutes(rootDir, routesDir, useTypeScript);\n return;\n }\n\n // 1. Move routable route entries under the locale segment (idempotent).\n const restructureResult = await restructureRoutesIntoLocale(\n rootDir,\n routesDir,\n targetLocaleSegment\n );\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured routes under ${colorizePath(join(routesDir, targetLocaleSegment))}: ${restructureResult.movedEntries\n .map((entry) => colorize(entry, ANSIColors.MAGENTA))\n .join(', ')}`\n );\n } else if (restructureResult.status === 'already-structured') {\n logger(\n `${v} ${colorizePath(join(routesDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // Scaffold the locale route for the segment actually in use: a pre-existing\n // segment (e.g. a prefix-all `$locale` the user already set up) wins over the\n // target, so we never create a second, conflicting locale segment.\n const activeLocaleSegment =\n restructureResult.status === 'already-structured'\n ? restructureResult.localeSegment\n : targetLocaleSegment;\n\n const localeDir = join(routesDir, activeLocaleSegment);\n await ensureDirectory(rootDir, localeDir);\n\n // 2. Locale segment route (`<segment>/route.tsx`) — create when absent.\n await createIfMissing(\n rootDir,\n join(localeDir, `route.${scriptExtension}`),\n buildLocaleRouteTemplate(activeLocaleSegment),\n 'locale route'\n );\n\n // 3. Root document — transform an existing `__root`, else scaffold one.\n const existingRoot = await findAppFile(rootDir, routesDir, '__root');\n if (existingRoot) {\n await transformExistingFile(rootDir, existingRoot, (code) =>\n wrapRootWithProvider(code, activeLocaleSegment)\n );\n } else {\n await createIfMissing(\n rootDir,\n join(routesDir, `__root.${scriptExtension}`),\n useTypeScript\n ? buildRootTemplateTs(activeLocaleSegment)\n : buildRootTemplateJs(activeLocaleSegment),\n 'root document'\n );\n }\n },\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { rewriteRelativeImports } from "../nextAppRouter/restructure.mjs";
|
|
2
|
-
import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
3
2
|
import { join, relative } from "node:path";
|
|
4
3
|
import fg from "fast-glob";
|
|
4
|
+
import { mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
5
5
|
|
|
6
6
|
//#region src/init/frameworkSetup/tanstackStart/restructure.ts
|
|
7
7
|
/** Source file extensions whose relative imports must be rewritten after a move. */
|
|
@@ -103,7 +103,62 @@ function RootDocument({ children }) {
|
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
`;
|
|
106
|
+
/**
|
|
107
|
+
* Module-scope server function resolving the request locale from the locale
|
|
108
|
+
* cookie / headers (`Accept-Language` fallback). Shared by the unprefixed root
|
|
109
|
+
* template and the root transform.
|
|
110
|
+
*/
|
|
111
|
+
const REQUEST_LOCALE_SERVER_FUNCTION = `const getRequestLocale = createServerFn().handler(() =>
|
|
112
|
+
getLocale({
|
|
113
|
+
getCookie: (name) => getCookie(name, getRequestHeader("cookie")),
|
|
114
|
+
getHeader: (name) => getRequestHeader(name),
|
|
115
|
+
})
|
|
116
|
+
);`;
|
|
117
|
+
/**
|
|
118
|
+
* Root route document (`routes/__root.tsx`) for routing modes without a locale
|
|
119
|
+
* path segment (`no-prefix`, `search-params`, or no proxy). The locale is
|
|
120
|
+
* resolved on the server per request and exposed through the root loader, so
|
|
121
|
+
* server and client render the same locale. Identical for TypeScript and
|
|
122
|
+
* JavaScript projects apart from the `children` prop type.
|
|
123
|
+
*/
|
|
124
|
+
const buildUnprefixedRootTemplate = (childrenPropType) => `import {
|
|
125
|
+
createRootRoute,
|
|
126
|
+
HeadContent,
|
|
127
|
+
Scripts,
|
|
128
|
+
} from "@tanstack/react-router";
|
|
129
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
130
|
+
import { getRequestHeader } from "@tanstack/react-start/server";
|
|
131
|
+
import { getCookie, getHTMLTextDir, getLocale } from "intlayer";${childrenPropType ? "\nimport type { ReactNode } from \"react\";" : ""}
|
|
132
|
+
import { IntlayerProvider } from "react-intlayer";
|
|
133
|
+
|
|
134
|
+
${REQUEST_LOCALE_SERVER_FUNCTION}
|
|
135
|
+
|
|
136
|
+
export const Route = createRootRoute({
|
|
137
|
+
loader: () => getRequestLocale(),
|
|
138
|
+
shellComponent: RootDocument,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
function RootDocument({ children }${childrenPropType}) {
|
|
142
|
+
const locale = Route.useLoaderData();
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<html dir={getHTMLTextDir(locale)} lang={locale}>
|
|
146
|
+
<head>
|
|
147
|
+
<HeadContent />
|
|
148
|
+
</head>
|
|
149
|
+
<body>
|
|
150
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
151
|
+
<Scripts />
|
|
152
|
+
</body>
|
|
153
|
+
</html>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
/** {@link buildUnprefixedRootTemplate} for TypeScript projects. */
|
|
158
|
+
const UNPREFIXED_ROOT_TEMPLATE_TS = buildUnprefixedRootTemplate(": { children: ReactNode }");
|
|
159
|
+
/** {@link buildUnprefixedRootTemplate} for JavaScript projects. */
|
|
160
|
+
const UNPREFIXED_ROOT_TEMPLATE_JS = buildUnprefixedRootTemplate("");
|
|
106
161
|
|
|
107
162
|
//#endregion
|
|
108
|
-
export { buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs };
|
|
163
|
+
export { REQUEST_LOCALE_SERVER_FUNCTION, UNPREFIXED_ROOT_TEMPLATE_JS, UNPREFIXED_ROOT_TEMPLATE_TS, buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs };
|
|
109
164
|
//# sourceMappingURL=templates.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh TanStack Start integration. These are only written\n * when the target file does not exist — they never overwrite user code.\n */\n\n/**\n * Builds the locale segment route (`routes/<localeSegment>/route.tsx`) for the\n * given segment. The optional `{-$locale}` param makes the default locale\n * prefix-free, while the required `$locale` param (`prefix-all`) prefixes every\n * locale; in both cases `validatePrefix` redirects unknown prefixes to a valid\n * one. Identical for TypeScript and JavaScript projects.\n */\nexport const buildLocaleRouteTemplate = (\n localeSegment: string\n): string => `import { createFileRoute, Outlet, redirect } from \"@tanstack/react-router\";\nimport { validatePrefix } from \"intlayer\";\n\nexport const Route = createFileRoute(\"/${localeSegment}\")({\n beforeLoad: ({ params }) => {\n // beforeLoad runs on both client and server, so resolve the locale from the\n // route params rather than from request headers.\n const { isValid, localePrefix } = validatePrefix(params.locale);\n\n // A valid (or absent, i.e. default) locale prefix is fine as-is.\n if (isValid) return;\n\n // Otherwise redirect to the same route with a valid locale prefix.\n throw redirect({\n params: { locale: localePrefix },\n to: \"/${localeSegment}\",\n });\n },\n component: Outlet,\n});\n`;\n\n/**\n * Root route document (`routes/__root.tsx`) providing `<html>` + the Intlayer\n * provider, with the locale read from the locale segment route params.\n *\n * The route is reached through `getRouteApi(<route id>)` rather than by\n * importing `<segment>/route`, so the root never imports a child route module —\n * the recommended way to consume another route's typed hooks.\n */\nexport const buildRootTemplateTs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport type { ReactNode } from \"react\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n/** JavaScript counterpart of {@link buildRootTemplateTs}. */\nexport const buildRootTemplateJs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,4BACX,kBACW;;;yCAG4B,cAAc;;;;;;;;;;;;cAYzC,cAAc;;;;;;;;;;;;;;AAe5B,MAAa,uBAAuB,kBAAkC;;;;;;;;;;oCAUlC,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAwBlD,MAAa,uBAAuB,kBAAkC;;;;;;;;;oCASlC,cAAc"}
|
|
1
|
+
{"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh TanStack Start integration. These are only written\n * when the target file does not exist — they never overwrite user code.\n */\n\n/**\n * Builds the locale segment route (`routes/<localeSegment>/route.tsx`) for the\n * given segment. The optional `{-$locale}` param makes the default locale\n * prefix-free, while the required `$locale` param (`prefix-all`) prefixes every\n * locale; in both cases `validatePrefix` redirects unknown prefixes to a valid\n * one. Identical for TypeScript and JavaScript projects.\n */\nexport const buildLocaleRouteTemplate = (\n localeSegment: string\n): string => `import { createFileRoute, Outlet, redirect } from \"@tanstack/react-router\";\nimport { validatePrefix } from \"intlayer\";\n\nexport const Route = createFileRoute(\"/${localeSegment}\")({\n beforeLoad: ({ params }) => {\n // beforeLoad runs on both client and server, so resolve the locale from the\n // route params rather than from request headers.\n const { isValid, localePrefix } = validatePrefix(params.locale);\n\n // A valid (or absent, i.e. default) locale prefix is fine as-is.\n if (isValid) return;\n\n // Otherwise redirect to the same route with a valid locale prefix.\n throw redirect({\n params: { locale: localePrefix },\n to: \"/${localeSegment}\",\n });\n },\n component: Outlet,\n});\n`;\n\n/**\n * Root route document (`routes/__root.tsx`) providing `<html>` + the Intlayer\n * provider, with the locale read from the locale segment route params.\n *\n * The route is reached through `getRouteApi(<route id>)` rather than by\n * importing `<segment>/route`, so the root never imports a child route module —\n * the recommended way to consume another route's typed hooks.\n */\nexport const buildRootTemplateTs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport type { ReactNode } from \"react\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n/** JavaScript counterpart of {@link buildRootTemplateTs}. */\nexport const buildRootTemplateJs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n/**\n * Module-scope server function resolving the request locale from the locale\n * cookie / headers (`Accept-Language` fallback). Shared by the unprefixed root\n * template and the root transform.\n */\nexport const REQUEST_LOCALE_SERVER_FUNCTION = `const getRequestLocale = createServerFn().handler(() =>\n getLocale({\n getCookie: (name) => getCookie(name, getRequestHeader(\"cookie\")),\n getHeader: (name) => getRequestHeader(name),\n })\n);`;\n\n/**\n * Root route document (`routes/__root.tsx`) for routing modes without a locale\n * path segment (`no-prefix`, `search-params`, or no proxy). The locale is\n * resolved on the server per request and exposed through the root loader, so\n * server and client render the same locale. Identical for TypeScript and\n * JavaScript projects apart from the `children` prop type.\n */\nconst buildUnprefixedRootTemplate = (\n childrenPropType: string\n): string => `import {\n createRootRoute,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { createServerFn } from \"@tanstack/react-start\";\nimport { getRequestHeader } from \"@tanstack/react-start/server\";\nimport { getCookie, getHTMLTextDir, getLocale } from \"intlayer\";${childrenPropType ? '\\nimport type { ReactNode } from \"react\";' : ''}\nimport { IntlayerProvider } from \"react-intlayer\";\n\n${REQUEST_LOCALE_SERVER_FUNCTION}\n\nexport const Route = createRootRoute({\n loader: () => getRequestLocale(),\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }${childrenPropType}) {\n const locale = Route.useLoaderData();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n/** {@link buildUnprefixedRootTemplate} for TypeScript projects. */\nexport const UNPREFIXED_ROOT_TEMPLATE_TS = buildUnprefixedRootTemplate(\n ': { children: ReactNode }'\n);\n\n/** {@link buildUnprefixedRootTemplate} for JavaScript projects. */\nexport const UNPREFIXED_ROOT_TEMPLATE_JS = buildUnprefixedRootTemplate('');\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,4BACX,kBACW;;;yCAG4B,cAAc;;;;;;;;;;;;cAYzC,cAAc;;;;;;;;;;;;;;AAe5B,MAAa,uBAAuB,kBAAkC;;;;;;;;;;oCAUlC,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAwBlD,MAAa,uBAAuB,kBAAkC;;;;;;;;;oCASlC,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BlD,MAAa,iCAAiC;;;;;;;;;;;;;AAc9C,MAAM,+BACJ,qBACW;;;;;;;kEAOqD,mBAAmB,gDAA8C,GAAG;;;EAGpI,+BAA+B;;;;;;;oCAOG,iBAAiB;;;;;;;;;;;;;;;;;AAkBrD,MAAa,8BAA8B,4BACzC,2BACF;;AAGA,MAAa,8BAA8B,4BAA4B,EAAE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { babelTsParser } from "../../../utils/babelParser.mjs";
|
|
2
2
|
import { ensureNamedImport, firstInsertIndex, isModuleScopeBinding } from "../../utils/astImports.mjs";
|
|
3
|
+
import { REQUEST_LOCALE_SERVER_FUNCTION } from "./templates.mjs";
|
|
3
4
|
import * as recast from "recast";
|
|
4
5
|
|
|
5
6
|
//#region src/init/frameworkSetup/tanstackStart/transforms.ts
|
|
@@ -122,6 +123,97 @@ const setHtmlLangAndDir = (ast) => {
|
|
|
122
123
|
} });
|
|
123
124
|
};
|
|
124
125
|
/**
|
|
126
|
+
* Wraps the first `{children}` expression inside `funcNode` with
|
|
127
|
+
* `<IntlayerProvider locale={locale}>`. Returns whether a wrap happened.
|
|
128
|
+
*/
|
|
129
|
+
const wrapChildrenWithProvider = (funcNode) => {
|
|
130
|
+
let wrapped = false;
|
|
131
|
+
recast.visit(funcNode, { visitJSXExpressionContainer(path) {
|
|
132
|
+
if (wrapped) return false;
|
|
133
|
+
const expression = path.node.expression;
|
|
134
|
+
if (expression?.type === "Identifier" && expression.name === "children") {
|
|
135
|
+
const providerElement = parseTsx("const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;").program.body[0].declarations[0].init;
|
|
136
|
+
providerElement.children = [path.node];
|
|
137
|
+
path.replace(providerElement);
|
|
138
|
+
wrapped = true;
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
this.traverse(path);
|
|
142
|
+
} });
|
|
143
|
+
return wrapped;
|
|
144
|
+
};
|
|
145
|
+
/** Whether `funcNode` already binds `locale` in its body or parameters. */
|
|
146
|
+
const bindsLocale = (funcNode) => funcNode.body?.type === "BlockStatement" && funcNode.body.body.some((stmt) => stmt.type === "VariableDeclaration" && stmt.declarations.some((d) => patternBindsName(d.id, "locale"))) || (funcNode.params ?? []).some((param) => patternBindsName(param, "locale"));
|
|
147
|
+
/**
|
|
148
|
+
* Finds the options object of the root route declaration —
|
|
149
|
+
* `createRootRoute({…})` or `createRootRouteWithContext<…>()({…})`. Returns
|
|
150
|
+
* `null` when absent or not an object literal.
|
|
151
|
+
*/
|
|
152
|
+
const findRootRouteOptions = (ast) => {
|
|
153
|
+
let options = null;
|
|
154
|
+
recast.visit(ast, { visitCallExpression(path) {
|
|
155
|
+
const { callee, arguments: args } = path.node;
|
|
156
|
+
const isCreateRootRoute = callee.type === "Identifier" && callee.name === "createRootRoute";
|
|
157
|
+
const isCreateRootRouteWithContext = callee.type === "CallExpression" && callee.callee?.type === "Identifier" && callee.callee.name === "createRootRouteWithContext";
|
|
158
|
+
if ((isCreateRootRoute || isCreateRootRouteWithContext) && args[0]?.type === "ObjectExpression") {
|
|
159
|
+
options = args[0];
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
this.traverse(path);
|
|
163
|
+
} });
|
|
164
|
+
return options;
|
|
165
|
+
};
|
|
166
|
+
/** Module-scope identifier of the injected request-locale server function. */
|
|
167
|
+
const REQUEST_LOCALE_FUNCTION_NAME = "getRequestLocale";
|
|
168
|
+
/**
|
|
169
|
+
* Wraps the `{children}` of a TanStack Start root document with
|
|
170
|
+
* `IntlayerProvider` for routing modes without a locale path segment. The
|
|
171
|
+
* locale is resolved per request by a server function exposed through the root
|
|
172
|
+
* `loader`, and read with `Route.useLoaderData()`. Safe and idempotent: bails
|
|
173
|
+
* when the provider is already present, when no `<html>` document or
|
|
174
|
+
* `{children}` is found, or when the root route cannot safely take a loader
|
|
175
|
+
* (no `Route` binding, no options object, or an existing `loader`).
|
|
176
|
+
*
|
|
177
|
+
* @param code Source of the project's `__root` document.
|
|
178
|
+
*/
|
|
179
|
+
const wrapRootWithRequestLocaleProvider = (code) => {
|
|
180
|
+
if (code.includes("IntlayerProvider")) return {
|
|
181
|
+
code,
|
|
182
|
+
status: "already"
|
|
183
|
+
};
|
|
184
|
+
const ast = parseTsx(code);
|
|
185
|
+
const funcNode = findHtmlDocumentFunction(ast);
|
|
186
|
+
const rootRouteOptions = findRootRouteOptions(ast);
|
|
187
|
+
const hasLoader = rootRouteOptions?.properties.some((property) => (property.key?.name ?? property.key?.value) === "loader");
|
|
188
|
+
if (!funcNode || !rootRouteOptions || hasLoader || !isModuleScopeBinding(ast, "Route")) return {
|
|
189
|
+
code,
|
|
190
|
+
status: "skipped"
|
|
191
|
+
};
|
|
192
|
+
if (!wrapChildrenWithProvider(funcNode)) return {
|
|
193
|
+
code,
|
|
194
|
+
status: "skipped"
|
|
195
|
+
};
|
|
196
|
+
if (!bindsLocale(funcNode)) {
|
|
197
|
+
if (funcNode.body?.type !== "BlockStatement") funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);
|
|
198
|
+
funcNode.body.body.unshift(parseTsx("const locale = Route.useLoaderData();").program.body[0]);
|
|
199
|
+
}
|
|
200
|
+
rootRouteOptions.properties.unshift(b.property("init", b.identifier("loader"), b.arrowFunctionExpression([], b.callExpression(b.identifier(REQUEST_LOCALE_FUNCTION_NAME), []))));
|
|
201
|
+
const insertsServerFunction = !isModuleScopeBinding(ast, REQUEST_LOCALE_FUNCTION_NAME);
|
|
202
|
+
if (insertsServerFunction) ast.program.body.splice(firstInsertIndex(ast), 0, parseTsx(REQUEST_LOCALE_SERVER_FUNCTION).program.body[0]);
|
|
203
|
+
ensureNamedImport(ast, "IntlayerProvider", "react-intlayer");
|
|
204
|
+
ensureNamedImport(ast, "createServerFn", "@tanstack/react-start");
|
|
205
|
+
ensureNamedImport(ast, "getRequestHeader", "@tanstack/react-start/server");
|
|
206
|
+
ensureNamedImport(ast, "getCookie", "intlayer");
|
|
207
|
+
ensureNamedImport(ast, "getLocale", "intlayer");
|
|
208
|
+
ensureNamedImport(ast, "getHTMLTextDir", "intlayer");
|
|
209
|
+
setHtmlLangAndDir(ast);
|
|
210
|
+
const printedCode = recast.print(ast).code;
|
|
211
|
+
return {
|
|
212
|
+
code: insertsServerFunction ? printedCode.replace(new RegExp(`([^\\n])\\n(const ${REQUEST_LOCALE_FUNCTION_NAME} = )`), "$1\n\n$2") : printedCode,
|
|
213
|
+
status: "wrapped"
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
125
217
|
* Wraps the `{children}` of a TanStack Start root document with
|
|
126
218
|
* `IntlayerProvider`, deriving the locale from the locale segment route params.
|
|
127
219
|
* Safe and idempotent: bails (returns the original code) when the provider is
|
|
@@ -143,20 +235,7 @@ const wrapRootWithProvider = (code, localeSegment) => {
|
|
|
143
235
|
code,
|
|
144
236
|
status: "skipped"
|
|
145
237
|
};
|
|
146
|
-
|
|
147
|
-
recast.visit(funcNode, { visitJSXExpressionContainer(path) {
|
|
148
|
-
if (wrapped) return false;
|
|
149
|
-
const expression = path.node.expression;
|
|
150
|
-
if (expression?.type === "Identifier" && expression.name === "children") {
|
|
151
|
-
const providerElement = parseTsx("const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;").program.body[0].declarations[0].init;
|
|
152
|
-
providerElement.children = [path.node];
|
|
153
|
-
path.replace(providerElement);
|
|
154
|
-
wrapped = true;
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
this.traverse(path);
|
|
158
|
-
} });
|
|
159
|
-
if (!wrapped) return {
|
|
238
|
+
if (!wrapChildrenWithProvider(funcNode)) return {
|
|
160
239
|
code,
|
|
161
240
|
status: "skipped"
|
|
162
241
|
};
|
|
@@ -178,5 +257,5 @@ const wrapRootWithProvider = (code, localeSegment) => {
|
|
|
178
257
|
};
|
|
179
258
|
|
|
180
259
|
//#endregion
|
|
181
|
-
export { wrapRootWithProvider };
|
|
260
|
+
export { wrapRootWithProvider, wrapRootWithRequestLocaleProvider };
|
|
182
261
|
//# sourceMappingURL=transforms.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transforms.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\nimport { babelTsParser } from '../../../utils/babelParser';\nimport {\n ensureNamedImport,\n firstInsertIndex,\n isModuleScopeBinding,\n} from '../../utils/astImports';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** Module-scope identifier bound to the locale route's typed API. */\nconst LOCALE_ROUTE_API_VARIABLE = 'localeRoute';\n\n/** babel-ts parser handles TypeScript *and* JSX. */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: babelTsParser });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped';\n};\n\n/**\n * Finds the single function that returns the `<html>` document element. Returns\n * `null` when there is no such function, or more than one (ambiguous).\n */\nconst findHtmlDocumentFunction = (ast: any): any => {\n const found = new Set<any>();\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n // Walk up to the nearest enclosing function node.\n let current = path.parentPath;\n while (current) {\n const value = current.value;\n if (\n value &&\n (n.FunctionDeclaration.check(value) ||\n n.FunctionExpression.check(value) ||\n n.ArrowFunctionExpression.check(value))\n ) {\n found.add(value);\n break;\n }\n current = current.parentPath;\n }\n }\n this.traverse(path);\n },\n });\n\n return found.size === 1 ? [...found][0] : null;\n};\n\n/**\n * Whether the binding pattern `node` introduces a variable named `name`.\n * Recurses through destructuring patterns so all of `const locale`,\n * `const { locale } = …`, `const { locale = fallback } = …`,\n * `const { foo: locale } = …`, `const [locale] = …` and `...locale` are\n * detected — not just a bare identifier.\n */\nconst patternBindsName = (node: any, name: string): boolean => {\n if (!node) return false;\n switch (node.type) {\n case 'Identifier':\n return node.name === name;\n case 'AssignmentPattern':\n // `locale = fallback`\n return patternBindsName(node.left, name);\n case 'RestElement':\n // `...locale`\n return patternBindsName(node.argument, name);\n case 'ArrayPattern':\n return node.elements.some((element: any) =>\n patternBindsName(element, name)\n );\n case 'ObjectPattern':\n return node.properties.some((property: any) =>\n property.type === 'RestElement'\n ? patternBindsName(property.argument, name)\n : // The *value* is the binding target (`{ foo: locale }` binds `locale`).\n patternBindsName(property.value, name)\n );\n default:\n return false;\n }\n};\n\n/**\n * Inserts `const { locale = defaultLocale } = localeRoute.useParams();` once, at\n * the top of the function body. The destructuring form binds only `locale`, so\n * it can never collide with a `params` variable the document already declares.\n * Expression-bodied arrow functions are first converted to a block so the\n * declaration has somewhere to live.\n *\n * No-ops when `locale` is already bound in the function — whether as a simple\n * identifier, through a destructuring pattern (e.g.\n * `const { locale = defaultLocale } = Route.useLoaderData()`), or as a function\n * parameter — so the injected declaration never collides with one the document\n * already provides (which would produce an\n * `Identifier 'locale' has already been declared` parse error).\n *\n * @returns `true` when the declaration was injected, `false` when the document\n * already binds `locale` itself. Callers use this to skip the imports and the\n * route API declaration the injected statement would have needed.\n */\nconst ensureLocaleFromParams = (funcNode: any): boolean => {\n if (funcNode.body?.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const declaredInBody = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some((d: any) => patternBindsName(d.id, 'locale'))\n );\n const declaredInParams = (funcNode.params ?? []).some((param: any) =>\n patternBindsName(param, 'locale')\n );\n if (declaredInBody || declaredInParams) return false;\n\n const statement = parseTsx(\n `const { locale = defaultLocale } = ${LOCALE_ROUTE_API_VARIABLE}.useParams();`\n ).program.body[0];\n funcNode.body.body.unshift(statement);\n return true;\n};\n\n/**\n * Ensures the module-scope `const localeRoute = getRouteApi(\"/<segment>\");`\n * declaration the injected locale statement reads from, inserted right after the\n * import block.\n *\n * Referencing the locale route by id keeps the root document free of an import\n * of `<segment>/route` — importing a child route module from `__root` is\n * discouraged, since the generated route tree already imports the root.\n *\n * No-ops when `localeRoute` is already bound at module scope, reusing that\n * binding rather than redeclaring it (which would be a parse error).\n *\n * @returns `true` when the declaration was inserted, `false` when an existing\n * binding was reused.\n */\nconst ensureLocaleRouteApi = (ast: any, localeSegment: string): boolean => {\n if (isModuleScopeBinding(ast, LOCALE_ROUTE_API_VARIABLE)) return false;\n\n const declaration = parseTsx(\n `const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi(\"/${localeSegment}\");`\n ).program.body[0];\n ast.program.body.splice(firstInsertIndex(ast), 0, declaration);\n return true;\n};\n\n/**\n * Puts a blank line between the import block and the inserted route API\n * declaration — recast prints a spliced statement flush against the preceding\n * line, which reads as part of the imports.\n */\nconst separateRouteApiDeclaration = (printedCode: string): string =>\n printedCode.replace(\n new RegExp(\n `([^\\\\n])\\\\n(const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi\\\\()`\n ),\n '$1\\n\\n$2'\n );\n\n/** Sets `lang={locale}` and `dir={getHTMLTextDir(locale)}` on the first `<html>` element. */\nconst setHtmlLangAndDir = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'html') {\n this.traverse(path);\n return;\n }\n\n const upsertAttribute = (name: string, value: any): void => {\n const existing = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === name\n ) as any;\n if (existing) {\n existing.value = value;\n } else {\n node.attributes?.push(b.jsxAttribute(b.jsxIdentifier(name), value));\n }\n };\n\n upsertAttribute('lang', b.jsxExpressionContainer(b.identifier('locale')));\n upsertAttribute(\n 'dir',\n b.jsxExpressionContainer(\n b.callExpression(b.identifier('getHTMLTextDir'), [\n b.identifier('locale'),\n ])\n )\n );\n return false;\n },\n });\n};\n\n/**\n * Wraps the `{children}` of a TanStack Start root document with\n * `IntlayerProvider`, deriving the locale from the locale segment route params.\n * Safe and idempotent: bails (returns the original code) when the provider is\n * already present, when no `<html>` document function is found, or when there is\n * no `{children}` placeholder to wrap.\n *\n * @param code Source of the project's `__root` document.\n * @param localeSegment Locale segment directory in use — `{-$locale}` for every\n * routing mode but `prefix-all`, which uses the required `$locale`.\n */\nexport const wrapRootWithProvider = (\n code: string,\n localeSegment: string\n): TransformResult => {\n const ast = parseTsx(code);\n\n if (code.includes('IntlayerProvider')) return { code, status: 'already' };\n\n const funcNode = findHtmlDocumentFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n const template = parseTsx(\n 'const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;'\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [path.node];\n path.replace(providerElement);\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!wrapped) return { code, status: 'skipped' };\n\n const injectedLocaleDeclaration = ensureLocaleFromParams(funcNode);\n\n ensureNamedImport(ast, 'IntlayerProvider', 'react-intlayer');\n // Only needed by the injected declaration: a document that already derives its\n // own `locale` must not be given unused imports or a stray route API const.\n let insertedRouteApi = false;\n if (injectedLocaleDeclaration) {\n ensureNamedImport(ast, 'getRouteApi', '@tanstack/react-router');\n ensureNamedImport(ast, 'defaultLocale', 'intlayer');\n insertedRouteApi = ensureLocaleRouteApi(ast, localeSegment);\n }\n ensureNamedImport(ast, 'getHTMLTextDir', 'intlayer');\n setHtmlLangAndDir(ast);\n\n const printedCode = recast.print(ast).code;\n\n return {\n code: insertedRouteApi\n ? separateRouteApiDeclaration(printedCode)\n : printedCode,\n status: 'wrapped',\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transforms.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\nimport { babelTsParser } from '../../../utils/babelParser';\nimport {\n ensureNamedImport,\n firstInsertIndex,\n isModuleScopeBinding,\n} from '../../utils/astImports';\nimport { REQUEST_LOCALE_SERVER_FUNCTION } from './templates';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** Module-scope identifier bound to the locale route's typed API. */\nconst LOCALE_ROUTE_API_VARIABLE = 'localeRoute';\n\n/** babel-ts parser handles TypeScript *and* JSX. */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: babelTsParser });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped';\n};\n\n/**\n * Finds the single function that returns the `<html>` document element. Returns\n * `null` when there is no such function, or more than one (ambiguous).\n */\nconst findHtmlDocumentFunction = (ast: any): any => {\n const found = new Set<any>();\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n // Walk up to the nearest enclosing function node.\n let current = path.parentPath;\n while (current) {\n const value = current.value;\n if (\n value &&\n (n.FunctionDeclaration.check(value) ||\n n.FunctionExpression.check(value) ||\n n.ArrowFunctionExpression.check(value))\n ) {\n found.add(value);\n break;\n }\n current = current.parentPath;\n }\n }\n this.traverse(path);\n },\n });\n\n return found.size === 1 ? [...found][0] : null;\n};\n\n/**\n * Whether the binding pattern `node` introduces a variable named `name`.\n * Recurses through destructuring patterns so all of `const locale`,\n * `const { locale } = …`, `const { locale = fallback } = …`,\n * `const { foo: locale } = …`, `const [locale] = …` and `...locale` are\n * detected — not just a bare identifier.\n */\nconst patternBindsName = (node: any, name: string): boolean => {\n if (!node) return false;\n switch (node.type) {\n case 'Identifier':\n return node.name === name;\n case 'AssignmentPattern':\n // `locale = fallback`\n return patternBindsName(node.left, name);\n case 'RestElement':\n // `...locale`\n return patternBindsName(node.argument, name);\n case 'ArrayPattern':\n return node.elements.some((element: any) =>\n patternBindsName(element, name)\n );\n case 'ObjectPattern':\n return node.properties.some((property: any) =>\n property.type === 'RestElement'\n ? patternBindsName(property.argument, name)\n : // The *value* is the binding target (`{ foo: locale }` binds `locale`).\n patternBindsName(property.value, name)\n );\n default:\n return false;\n }\n};\n\n/**\n * Inserts `const { locale = defaultLocale } = localeRoute.useParams();` once, at\n * the top of the function body. The destructuring form binds only `locale`, so\n * it can never collide with a `params` variable the document already declares.\n * Expression-bodied arrow functions are first converted to a block so the\n * declaration has somewhere to live.\n *\n * No-ops when `locale` is already bound in the function — whether as a simple\n * identifier, through a destructuring pattern (e.g.\n * `const { locale = defaultLocale } = Route.useLoaderData()`), or as a function\n * parameter — so the injected declaration never collides with one the document\n * already provides (which would produce an\n * `Identifier 'locale' has already been declared` parse error).\n *\n * @returns `true` when the declaration was injected, `false` when the document\n * already binds `locale` itself. Callers use this to skip the imports and the\n * route API declaration the injected statement would have needed.\n */\nconst ensureLocaleFromParams = (funcNode: any): boolean => {\n if (funcNode.body?.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const declaredInBody = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some((d: any) => patternBindsName(d.id, 'locale'))\n );\n const declaredInParams = (funcNode.params ?? []).some((param: any) =>\n patternBindsName(param, 'locale')\n );\n if (declaredInBody || declaredInParams) return false;\n\n const statement = parseTsx(\n `const { locale = defaultLocale } = ${LOCALE_ROUTE_API_VARIABLE}.useParams();`\n ).program.body[0];\n funcNode.body.body.unshift(statement);\n return true;\n};\n\n/**\n * Ensures the module-scope `const localeRoute = getRouteApi(\"/<segment>\");`\n * declaration the injected locale statement reads from, inserted right after the\n * import block.\n *\n * Referencing the locale route by id keeps the root document free of an import\n * of `<segment>/route` — importing a child route module from `__root` is\n * discouraged, since the generated route tree already imports the root.\n *\n * No-ops when `localeRoute` is already bound at module scope, reusing that\n * binding rather than redeclaring it (which would be a parse error).\n *\n * @returns `true` when the declaration was inserted, `false` when an existing\n * binding was reused.\n */\nconst ensureLocaleRouteApi = (ast: any, localeSegment: string): boolean => {\n if (isModuleScopeBinding(ast, LOCALE_ROUTE_API_VARIABLE)) return false;\n\n const declaration = parseTsx(\n `const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi(\"/${localeSegment}\");`\n ).program.body[0];\n ast.program.body.splice(firstInsertIndex(ast), 0, declaration);\n return true;\n};\n\n/**\n * Puts a blank line between the import block and the inserted route API\n * declaration — recast prints a spliced statement flush against the preceding\n * line, which reads as part of the imports.\n */\nconst separateRouteApiDeclaration = (printedCode: string): string =>\n printedCode.replace(\n new RegExp(\n `([^\\\\n])\\\\n(const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi\\\\()`\n ),\n '$1\\n\\n$2'\n );\n\n/** Sets `lang={locale}` and `dir={getHTMLTextDir(locale)}` on the first `<html>` element. */\nconst setHtmlLangAndDir = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'html') {\n this.traverse(path);\n return;\n }\n\n const upsertAttribute = (name: string, value: any): void => {\n const existing = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === name\n ) as any;\n if (existing) {\n existing.value = value;\n } else {\n node.attributes?.push(b.jsxAttribute(b.jsxIdentifier(name), value));\n }\n };\n\n upsertAttribute('lang', b.jsxExpressionContainer(b.identifier('locale')));\n upsertAttribute(\n 'dir',\n b.jsxExpressionContainer(\n b.callExpression(b.identifier('getHTMLTextDir'), [\n b.identifier('locale'),\n ])\n )\n );\n return false;\n },\n });\n};\n\n/**\n * Wraps the first `{children}` expression inside `funcNode` with\n * `<IntlayerProvider locale={locale}>`. Returns whether a wrap happened.\n */\nconst wrapChildrenWithProvider = (funcNode: any): boolean => {\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n const template = parseTsx(\n 'const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;'\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [path.node];\n path.replace(providerElement);\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n return wrapped;\n};\n\n/** Whether `funcNode` already binds `locale` in its body or parameters. */\nconst bindsLocale = (funcNode: any): boolean =>\n (funcNode.body?.type === 'BlockStatement' &&\n funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some((d: any) => patternBindsName(d.id, 'locale'))\n )) ||\n (funcNode.params ?? []).some((param: any) =>\n patternBindsName(param, 'locale')\n );\n\n/**\n * Finds the options object of the root route declaration —\n * `createRootRoute({…})` or `createRootRouteWithContext<…>()({…})`. Returns\n * `null` when absent or not an object literal.\n */\nconst findRootRouteOptions = (ast: any): any => {\n let options: any = null;\n\n recast.visit(ast, {\n visitCallExpression(path) {\n const { callee, arguments: args } = path.node as any;\n const isCreateRootRoute =\n callee.type === 'Identifier' && callee.name === 'createRootRoute';\n const isCreateRootRouteWithContext =\n callee.type === 'CallExpression' &&\n callee.callee?.type === 'Identifier' &&\n callee.callee.name === 'createRootRouteWithContext';\n\n if (\n (isCreateRootRoute || isCreateRootRouteWithContext) &&\n args[0]?.type === 'ObjectExpression'\n ) {\n options = args[0];\n return false;\n }\n this.traverse(path);\n },\n });\n\n return options;\n};\n\n/** Module-scope identifier of the injected request-locale server function. */\nconst REQUEST_LOCALE_FUNCTION_NAME = 'getRequestLocale';\n\n/**\n * Wraps the `{children}` of a TanStack Start root document with\n * `IntlayerProvider` for routing modes without a locale path segment. The\n * locale is resolved per request by a server function exposed through the root\n * `loader`, and read with `Route.useLoaderData()`. Safe and idempotent: bails\n * when the provider is already present, when no `<html>` document or\n * `{children}` is found, or when the root route cannot safely take a loader\n * (no `Route` binding, no options object, or an existing `loader`).\n *\n * @param code Source of the project's `__root` document.\n */\nexport const wrapRootWithRequestLocaleProvider = (\n code: string\n): TransformResult => {\n if (code.includes('IntlayerProvider')) return { code, status: 'already' };\n\n const ast = parseTsx(code);\n\n const funcNode = findHtmlDocumentFunction(ast);\n const rootRouteOptions = findRootRouteOptions(ast);\n const hasLoader = rootRouteOptions?.properties.some(\n (property: any) => (property.key?.name ?? property.key?.value) === 'loader'\n );\n if (\n !funcNode ||\n !rootRouteOptions ||\n hasLoader ||\n !isModuleScopeBinding(ast, 'Route')\n ) {\n return { code, status: 'skipped' };\n }\n\n if (!wrapChildrenWithProvider(funcNode)) return { code, status: 'skipped' };\n\n if (!bindsLocale(funcNode)) {\n if (funcNode.body?.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n funcNode.body.body.unshift(\n parseTsx('const locale = Route.useLoaderData();').program.body[0]\n );\n }\n\n rootRouteOptions.properties.unshift(\n b.property(\n 'init',\n b.identifier('loader'),\n b.arrowFunctionExpression(\n [],\n b.callExpression(b.identifier(REQUEST_LOCALE_FUNCTION_NAME), [])\n )\n )\n );\n\n const insertsServerFunction = !isModuleScopeBinding(\n ast,\n REQUEST_LOCALE_FUNCTION_NAME\n );\n if (insertsServerFunction) {\n ast.program.body.splice(\n firstInsertIndex(ast),\n 0,\n parseTsx(REQUEST_LOCALE_SERVER_FUNCTION).program.body[0]\n );\n }\n\n ensureNamedImport(ast, 'IntlayerProvider', 'react-intlayer');\n ensureNamedImport(ast, 'createServerFn', '@tanstack/react-start');\n ensureNamedImport(ast, 'getRequestHeader', '@tanstack/react-start/server');\n ensureNamedImport(ast, 'getCookie', 'intlayer');\n ensureNamedImport(ast, 'getLocale', 'intlayer');\n ensureNamedImport(ast, 'getHTMLTextDir', 'intlayer');\n setHtmlLangAndDir(ast);\n\n const printedCode = recast.print(ast).code;\n\n return {\n // recast prints the spliced declaration flush against the imports.\n code: insertsServerFunction\n ? printedCode.replace(\n new RegExp(`([^\\\\n])\\\\n(const ${REQUEST_LOCALE_FUNCTION_NAME} = )`),\n '$1\\n\\n$2'\n )\n : printedCode,\n status: 'wrapped',\n };\n};\n\n/**\n * Wraps the `{children}` of a TanStack Start root document with\n * `IntlayerProvider`, deriving the locale from the locale segment route params.\n * Safe and idempotent: bails (returns the original code) when the provider is\n * already present, when no `<html>` document function is found, or when there is\n * no `{children}` placeholder to wrap.\n *\n * @param code Source of the project's `__root` document.\n * @param localeSegment Locale segment directory in use — `{-$locale}` for every\n * routing mode but `prefix-all`, which uses the required `$locale`.\n */\nexport const wrapRootWithProvider = (\n code: string,\n localeSegment: string\n): TransformResult => {\n const ast = parseTsx(code);\n\n if (code.includes('IntlayerProvider')) return { code, status: 'already' };\n\n const funcNode = findHtmlDocumentFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n if (!wrapChildrenWithProvider(funcNode)) return { code, status: 'skipped' };\n\n const injectedLocaleDeclaration = ensureLocaleFromParams(funcNode);\n\n ensureNamedImport(ast, 'IntlayerProvider', 'react-intlayer');\n // Only needed by the injected declaration: a document that already derives its\n // own `locale` must not be given unused imports or a stray route API const.\n let insertedRouteApi = false;\n if (injectedLocaleDeclaration) {\n ensureNamedImport(ast, 'getRouteApi', '@tanstack/react-router');\n ensureNamedImport(ast, 'defaultLocale', 'intlayer');\n insertedRouteApi = ensureLocaleRouteApi(ast, localeSegment);\n }\n ensureNamedImport(ast, 'getHTMLTextDir', 'intlayer');\n setHtmlLangAndDir(ast);\n\n const printedCode = recast.print(ast).code;\n\n return {\n code: insertedRouteApi\n ? separateRouteApiDeclaration(printedCode)\n : printedCode,\n status: 'wrapped',\n };\n};\n"],"mappings":";;;;;;AASA,MAAM,EAAE,UAAU,GAAG,YAAY,MAAM,OAAO;;AAG9C,MAAM,4BAA4B;;AAGlC,MAAM,YAAY,SAChB,OAAO,MAAM,MAAM,EAAE,QAAQ,cAAc,CAAC;;;;;AAY9C,MAAM,4BAA4B,QAAkB;CAClD,MAAM,wBAAQ,IAAI,IAAS;CAE3B,OAAO,MAAM,KAAK,EAChB,uBAAuB,MAAM;EAC3B,MAAM,OAAO,KAAK;EAClB,IAAI,KAAK,MAAM,SAAS,mBAAmB,KAAK,KAAK,SAAS,QAAQ;GAEpE,IAAI,UAAU,KAAK;GACnB,OAAO,SAAS;IACd,MAAM,QAAQ,QAAQ;IACtB,IACE,UACC,EAAE,oBAAoB,MAAM,KAAK,KAChC,EAAE,mBAAmB,MAAM,KAAK,KAChC,EAAE,wBAAwB,MAAM,KAAK,IACvC;KACA,MAAM,IAAI,KAAK;KACf;IACF;IACA,UAAU,QAAQ;GACpB;EACF;EACA,KAAK,SAAS,IAAI;CACpB,EACF,CAAC;CAED,OAAO,MAAM,SAAS,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK;AAC5C;;;;;;;;AASA,MAAM,oBAAoB,MAAW,SAA0B;CAC7D,IAAI,CAAC,MAAM,OAAO;CAClB,QAAQ,KAAK,MAAb;EACE,KAAK,cACH,OAAO,KAAK,SAAS;EACvB,KAAK,qBAEH,OAAO,iBAAiB,KAAK,MAAM,IAAI;EACzC,KAAK,eAEH,OAAO,iBAAiB,KAAK,UAAU,IAAI;EAC7C,KAAK,gBACH,OAAO,KAAK,SAAS,MAAM,YACzB,iBAAiB,SAAS,IAAI,CAChC;EACF,KAAK,iBACH,OAAO,KAAK,WAAW,MAAM,aAC3B,SAAS,SAAS,gBACd,iBAAiB,SAAS,UAAU,IAAI,IAExC,iBAAiB,SAAS,OAAO,IAAI,CAC3C;EACF,SACE,OAAO;CACX;AACF;;;;;;;;;;;;;;;;;;;AAoBA,MAAM,0BAA0B,aAA2B;CACzD,IAAI,SAAS,MAAM,SAAS,kBAC1B,SAAS,OAAO,EAAE,eAAe,CAAC,EAAE,gBAAgB,SAAS,IAAI,CAAC,CAAC;CAGrE,MAAM,iBAAiB,SAAS,KAAK,KAAK,MACvC,SACC,KAAK,SAAS,yBACd,KAAK,aAAa,MAAM,MAAW,iBAAiB,EAAE,IAAI,QAAQ,CAAC,CACvE;CACA,MAAM,oBAAoB,SAAS,UAAU,CAAC,EAAC,CAAE,MAAM,UACrD,iBAAiB,OAAO,QAAQ,CAClC;CACA,IAAI,kBAAkB,kBAAkB,OAAO;CAE/C,MAAM,YAAY,SAChB,sCAAsC,0BAA0B,cAClE,CAAC,CAAC,QAAQ,KAAK;CACf,SAAS,KAAK,KAAK,QAAQ,SAAS;CACpC,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,MAAM,wBAAwB,KAAU,kBAAmC;CACzE,IAAI,qBAAqB,KAAK,yBAAyB,GAAG,OAAO;CAEjE,MAAM,cAAc,SAClB,SAAS,0BAA0B,mBAAmB,cAAc,IACtE,CAAC,CAAC,QAAQ,KAAK;CACf,IAAI,QAAQ,KAAK,OAAO,iBAAiB,GAAG,GAAG,GAAG,WAAW;CAC7D,OAAO;AACT;;;;;;AAOA,MAAM,+BAA+B,gBACnC,YAAY,QACV,IAAI,OACF,qBAAqB,0BAA0B,mBACjD,GACA,UACF;;AAGF,MAAM,qBAAqB,QAAmB;CAC5C,OAAO,MAAM,KAAK,EAChB,uBAAuB,MAAM;EAC3B,MAAM,OAAO,KAAK;EAClB,IAAI,KAAK,MAAM,SAAS,mBAAmB,KAAK,KAAK,SAAS,QAAQ;GACpE,KAAK,SAAS,IAAI;GAClB;EACF;EAEA,MAAM,mBAAmB,MAAc,UAAqB;GAC1D,MAAM,WAAW,KAAK,YAAY,MAC/B,SACC,KAAK,SAAS,kBAAkB,KAAK,MAAM,SAAS,IACxD;GACA,IAAI,UACF,SAAS,QAAQ;QAEjB,KAAK,YAAY,KAAK,EAAE,aAAa,EAAE,cAAc,IAAI,GAAG,KAAK,CAAC;EAEtE;EAEA,gBAAgB,QAAQ,EAAE,uBAAuB,EAAE,WAAW,QAAQ,CAAC,CAAC;EACxE,gBACE,OACA,EAAE,uBACA,EAAE,eAAe,EAAE,WAAW,gBAAgB,GAAG,CAC/C,EAAE,WAAW,QAAQ,CACvB,CAAC,CACH,CACF;EACA,OAAO;CACT,EACF,CAAC;AACH;;;;;AAMA,MAAM,4BAA4B,aAA2B;CAC3D,IAAI,UAAU;CACd,OAAO,MAAM,UAAU,EACrB,4BAA4B,MAAM;EAChC,IAAI,SAAS,OAAO;EACpB,MAAM,aAAa,KAAK,KAAK;EAC7B,IAAI,YAAY,SAAS,gBAAgB,WAAW,SAAS,YAAY;GAIvE,MAAM,kBAHW,SACf,kFAE6B,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,aAAa,EAAE,CAAC;GACjE,gBAAgB,WAAW,CAAC,KAAK,IAAI;GACrC,KAAK,QAAQ,eAAe;GAC5B,UAAU;GACV,OAAO;EACT;EACA,KAAK,SAAS,IAAI;CACpB,EACF,CAAC;CACD,OAAO;AACT;;AAGA,MAAM,eAAe,aAClB,SAAS,MAAM,SAAS,oBACvB,SAAS,KAAK,KAAK,MAChB,SACC,KAAK,SAAS,yBACd,KAAK,aAAa,MAAM,MAAW,iBAAiB,EAAE,IAAI,QAAQ,CAAC,CACvE,MACD,SAAS,UAAU,CAAC,EAAC,CAAE,MAAM,UAC5B,iBAAiB,OAAO,QAAQ,CAClC;;;;;;AAOF,MAAM,wBAAwB,QAAkB;CAC9C,IAAI,UAAe;CAEnB,OAAO,MAAM,KAAK,EAChB,oBAAoB,MAAM;EACxB,MAAM,EAAE,QAAQ,WAAW,SAAS,KAAK;EACzC,MAAM,oBACJ,OAAO,SAAS,gBAAgB,OAAO,SAAS;EAClD,MAAM,+BACJ,OAAO,SAAS,oBAChB,OAAO,QAAQ,SAAS,gBACxB,OAAO,OAAO,SAAS;EAEzB,KACG,qBAAqB,iCACtB,KAAK,EAAE,EAAE,SAAS,oBAClB;GACA,UAAU,KAAK;GACf,OAAO;EACT;EACA,KAAK,SAAS,IAAI;CACpB,EACF,CAAC;CAED,OAAO;AACT;;AAGA,MAAM,+BAA+B;;;;;;;;;;;;AAarC,MAAa,qCACX,SACoB;CACpB,IAAI,KAAK,SAAS,kBAAkB,GAAG,OAAO;EAAE;EAAM,QAAQ;CAAU;CAExE,MAAM,MAAM,SAAS,IAAI;CAEzB,MAAM,WAAW,yBAAyB,GAAG;CAC7C,MAAM,mBAAmB,qBAAqB,GAAG;CACjD,MAAM,YAAY,kBAAkB,WAAW,MAC5C,cAAmB,SAAS,KAAK,QAAQ,SAAS,KAAK,WAAW,QACrE;CACA,IACE,CAAC,YACD,CAAC,oBACD,aACA,CAAC,qBAAqB,KAAK,OAAO,GAElC,OAAO;EAAE;EAAM,QAAQ;CAAU;CAGnC,IAAI,CAAC,yBAAyB,QAAQ,GAAG,OAAO;EAAE;EAAM,QAAQ;CAAU;CAE1E,IAAI,CAAC,YAAY,QAAQ,GAAG;EAC1B,IAAI,SAAS,MAAM,SAAS,kBAC1B,SAAS,OAAO,EAAE,eAAe,CAAC,EAAE,gBAAgB,SAAS,IAAI,CAAC,CAAC;EAErE,SAAS,KAAK,KAAK,QACjB,SAAS,uCAAuC,CAAC,CAAC,QAAQ,KAAK,EACjE;CACF;CAEA,iBAAiB,WAAW,QAC1B,EAAE,SACA,QACA,EAAE,WAAW,QAAQ,GACrB,EAAE,wBACA,CAAC,GACD,EAAE,eAAe,EAAE,WAAW,4BAA4B,GAAG,CAAC,CAAC,CACjE,CACF,CACF;CAEA,MAAM,wBAAwB,CAAC,qBAC7B,KACA,4BACF;CACA,IAAI,uBACF,IAAI,QAAQ,KAAK,OACf,iBAAiB,GAAG,GACpB,GACA,SAAS,8BAA8B,CAAC,CAAC,QAAQ,KAAK,EACxD;CAGF,kBAAkB,KAAK,oBAAoB,gBAAgB;CAC3D,kBAAkB,KAAK,kBAAkB,uBAAuB;CAChE,kBAAkB,KAAK,oBAAoB,8BAA8B;CACzE,kBAAkB,KAAK,aAAa,UAAU;CAC9C,kBAAkB,KAAK,aAAa,UAAU;CAC9C,kBAAkB,KAAK,kBAAkB,UAAU;CACnD,kBAAkB,GAAG;CAErB,MAAM,cAAc,OAAO,MAAM,GAAG,CAAC,CAAC;CAEtC,OAAO;EAEL,MAAM,wBACF,YAAY,QACV,IAAI,OAAO,qBAAqB,6BAA6B,KAAK,GAClE,UACF,IACA;EACJ,QAAQ;CACV;AACF;;;;;;;;;;;;AAaA,MAAa,wBACX,MACA,kBACoB;CACpB,MAAM,MAAM,SAAS,IAAI;CAEzB,IAAI,KAAK,SAAS,kBAAkB,GAAG,OAAO;EAAE;EAAM,QAAQ;CAAU;CAExE,MAAM,WAAW,yBAAyB,GAAG;CAC7C,IAAI,CAAC,UAAU,OAAO;EAAE;EAAM,QAAQ;CAAU;CAEhD,IAAI,CAAC,yBAAyB,QAAQ,GAAG,OAAO;EAAE;EAAM,QAAQ;CAAU;CAE1E,MAAM,4BAA4B,uBAAuB,QAAQ;CAEjE,kBAAkB,KAAK,oBAAoB,gBAAgB;CAG3D,IAAI,mBAAmB;CACvB,IAAI,2BAA2B;EAC7B,kBAAkB,KAAK,eAAe,wBAAwB;EAC9D,kBAAkB,KAAK,iBAAiB,UAAU;EAClD,mBAAmB,qBAAqB,KAAK,aAAa;CAC5D;CACA,kBAAkB,KAAK,kBAAkB,UAAU;CACnD,kBAAkB,GAAG;CAErB,MAAM,cAAc,OAAO,MAAM,GAAG,CAAC,CAAC;CAEtC,OAAO;EACL,MAAM,mBACF,4BAA4B,WAAW,IACvC;EACJ,QAAQ;CACV;AACF"}
|