@intlayer/engine 9.5.9 → 9.5.11
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/cli.cjs +7 -0
- package/dist/cjs/docReview/alignBlocks.cjs +53 -36
- package/dist/cjs/docReview/alignBlocks.cjs.map +1 -1
- package/dist/cjs/docReview/rebuildDocument.cjs +3 -1
- package/dist/cjs/docReview/rebuildDocument.cjs.map +1 -1
- package/dist/cjs/docReview/reviewReport.cjs.map +1 -1
- package/dist/cjs/docReview/segmentDocument.cjs +8 -5
- package/dist/cjs/docReview/segmentDocument.cjs.map +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/templates.cjs +56 -0
- package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs.map +1 -1
- package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +6 -3
- 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/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/index.cjs +10 -0
- 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 +62 -13
- 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 +4 -1
- package/dist/cjs/initConfig/index.cjs.map +1 -1
- package/dist/cjs/installMCP/installMCP.cjs +1 -1
- package/dist/cjs/installMCP/installMCP.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs +3 -1
- package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs +1 -1
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/log.cjs +1 -1
- package/dist/cjs/loadDictionaries/log.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs +111 -43
- package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs.map +1 -1
- package/dist/cjs/scan/analyzeBundleContent.cjs +26 -12
- package/dist/cjs/scan/analyzeBundleContent.cjs.map +1 -1
- package/dist/cjs/scan/calculateScore.cjs +4 -1
- package/dist/cjs/scan/calculateScore.cjs.map +1 -1
- package/dist/cjs/scan/checks.cjs +394 -221
- package/dist/cjs/scan/checks.cjs.map +1 -1
- package/dist/cjs/scan/detection/checkDetails.cjs +56 -0
- package/dist/cjs/scan/detection/checkDetails.cjs.map +1 -0
- package/dist/cjs/scan/detection/classifyInternalLinks.cjs +47 -0
- package/dist/cjs/scan/detection/classifyInternalLinks.cjs.map +1 -0
- package/dist/cjs/scan/detection/detectRoutingStrategy.cjs +143 -0
- package/dist/cjs/scan/detection/detectRoutingStrategy.cjs.map +1 -0
- package/dist/cjs/scan/detection/detectTechnologies.cjs +89 -0
- package/dist/cjs/scan/detection/detectTechnologies.cjs.map +1 -0
- package/dist/cjs/scan/detection/index.cjs +36 -0
- package/dist/cjs/scan/detection/localeCode.cjs +118 -0
- package/dist/cjs/scan/detection/localeCode.cjs.map +1 -0
- package/dist/cjs/scan/detection/localizedPages.cjs +48 -0
- package/dist/cjs/scan/detection/localizedPages.cjs.map +1 -0
- package/dist/cjs/scan/detection/technologySignatures.cjs +564 -0
- package/dist/cjs/scan/detection/technologySignatures.cjs.map +1 -0
- package/dist/cjs/scan/detection/url.cjs +53 -0
- package/dist/cjs/scan/detection/url.cjs.map +1 -0
- package/dist/cjs/scan/fetchText.cjs +56 -0
- package/dist/cjs/scan/fetchText.cjs.map +1 -0
- package/dist/cjs/scan/index.cjs +59 -3
- package/dist/cjs/scan/parseHtml.cjs +55 -10
- package/dist/cjs/scan/parseHtml.cjs.map +1 -1
- package/dist/cjs/scan/robots.cjs +65 -0
- package/dist/cjs/scan/robots.cjs.map +1 -0
- package/dist/cjs/scan/runScanChecks.cjs +89 -0
- package/dist/cjs/scan/runScanChecks.cjs.map +1 -0
- package/dist/cjs/scan/scanWebsite.cjs +58 -52
- package/dist/cjs/scan/scanWebsite.cjs.map +1 -1
- package/dist/cjs/scan/sitemap.cjs +109 -0
- package/dist/cjs/scan/sitemap.cjs.map +1 -0
- package/dist/cjs/utils/chunkJSON.cjs +24 -9
- package/dist/cjs/utils/chunkJSON.cjs.map +1 -1
- package/dist/cjs/utils/getChunk.cjs +2 -2
- package/dist/cjs/utils/getChunk.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/index.cjs +1 -1
- package/dist/cjs/utils/runParallel/index.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/pidTree.cjs +23 -11
- package/dist/cjs/utils/runParallel/pidTree.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/ps.cjs +8 -3
- package/dist/cjs/utils/runParallel/ps.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/runTask.cjs +2 -1
- package/dist/cjs/utils/runParallel/runTask.cjs.map +1 -1
- package/dist/cjs/utils/runParallel/wmic.cjs +8 -3
- package/dist/cjs/utils/runParallel/wmic.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs +7 -4
- package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +2 -2
- package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs.map +1 -1
- package/dist/esm/cli.mjs +5 -3
- package/dist/esm/docReview/alignBlocks.mjs +53 -36
- package/dist/esm/docReview/alignBlocks.mjs.map +1 -1
- package/dist/esm/docReview/rebuildDocument.mjs +3 -1
- package/dist/esm/docReview/rebuildDocument.mjs.map +1 -1
- package/dist/esm/docReview/reviewReport.mjs.map +1 -1
- package/dist/esm/docReview/segmentDocument.mjs +8 -5
- package/dist/esm/docReview/segmentDocument.mjs.map +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/templates.mjs +54 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/templates.mjs.map +1 -1
- package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +6 -3
- 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/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/index.mjs +5 -3
- 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 +60 -14
- 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 +4 -1
- package/dist/esm/initConfig/index.mjs.map +1 -1
- package/dist/esm/installMCP/installMCP.mjs +1 -1
- package/dist/esm/installMCP/installMCP.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadLocalDictionaries.mjs +3 -1
- package/dist/esm/loadDictionaries/loadLocalDictionaries.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs +1 -1
- package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs.map +1 -1
- package/dist/esm/loadDictionaries/log.mjs +1 -1
- package/dist/esm/loadDictionaries/log.mjs.map +1 -1
- package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs +112 -44
- package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs.map +1 -1
- package/dist/esm/scan/analyzeBundleContent.mjs +26 -12
- package/dist/esm/scan/analyzeBundleContent.mjs.map +1 -1
- package/dist/esm/scan/calculateScore.mjs +4 -1
- package/dist/esm/scan/calculateScore.mjs.map +1 -1
- package/dist/esm/scan/checks.mjs +387 -220
- package/dist/esm/scan/checks.mjs.map +1 -1
- package/dist/esm/scan/detection/checkDetails.mjs +54 -0
- package/dist/esm/scan/detection/checkDetails.mjs.map +1 -0
- package/dist/esm/scan/detection/classifyInternalLinks.mjs +46 -0
- package/dist/esm/scan/detection/classifyInternalLinks.mjs.map +1 -0
- package/dist/esm/scan/detection/detectRoutingStrategy.mjs +141 -0
- package/dist/esm/scan/detection/detectRoutingStrategy.mjs.map +1 -0
- package/dist/esm/scan/detection/detectTechnologies.mjs +87 -0
- package/dist/esm/scan/detection/detectTechnologies.mjs.map +1 -0
- package/dist/esm/scan/detection/index.mjs +10 -0
- package/dist/esm/scan/detection/localeCode.mjs +108 -0
- package/dist/esm/scan/detection/localeCode.mjs.map +1 -0
- package/dist/esm/scan/detection/localizedPages.mjs +46 -0
- package/dist/esm/scan/detection/localizedPages.mjs.map +1 -0
- package/dist/esm/scan/detection/technologySignatures.mjs +563 -0
- package/dist/esm/scan/detection/technologySignatures.mjs.map +1 -0
- package/dist/esm/scan/detection/url.mjs +47 -0
- package/dist/esm/scan/detection/url.mjs.map +1 -0
- package/dist/esm/scan/fetchText.mjs +55 -0
- package/dist/esm/scan/fetchText.mjs.map +1 -0
- package/dist/esm/scan/index.mjs +15 -3
- package/dist/esm/scan/parseHtml.mjs +52 -11
- package/dist/esm/scan/parseHtml.mjs.map +1 -1
- package/dist/esm/scan/robots.mjs +63 -0
- package/dist/esm/scan/robots.mjs.map +1 -0
- package/dist/esm/scan/runScanChecks.mjs +88 -0
- package/dist/esm/scan/runScanChecks.mjs.map +1 -0
- package/dist/esm/scan/scanWebsite.mjs +59 -53
- package/dist/esm/scan/scanWebsite.mjs.map +1 -1
- package/dist/esm/scan/sitemap.mjs +105 -0
- package/dist/esm/scan/sitemap.mjs.map +1 -0
- package/dist/esm/utils/chunkJSON.mjs +24 -9
- package/dist/esm/utils/chunkJSON.mjs.map +1 -1
- package/dist/esm/utils/getChunk.mjs +2 -2
- package/dist/esm/utils/getChunk.mjs.map +1 -1
- package/dist/esm/utils/runParallel/index.mjs +1 -1
- package/dist/esm/utils/runParallel/index.mjs.map +1 -1
- package/dist/esm/utils/runParallel/pidTree.mjs +23 -11
- package/dist/esm/utils/runParallel/pidTree.mjs.map +1 -1
- package/dist/esm/utils/runParallel/ps.mjs +8 -3
- package/dist/esm/utils/runParallel/ps.mjs.map +1 -1
- package/dist/esm/utils/runParallel/runTask.mjs +2 -1
- package/dist/esm/utils/runParallel/runTask.mjs.map +1 -1
- package/dist/esm/utils/runParallel/wmic.mjs +8 -3
- package/dist/esm/utils/runParallel/wmic.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/detectExportedComponentName.mjs +7 -4
- package/dist/esm/writeContentDeclaration/detectExportedComponentName.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +2 -2
- package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs.map +1 -1
- package/dist/types/cli.d.ts +5 -3
- package/dist/types/docReview/rebuildDocument.d.ts.map +1 -1
- package/dist/types/docReview/reviewReport.d.ts.map +1 -1
- package/dist/types/docReview/segmentDocument.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/dist/types/scan/analyzeBundleContent.d.ts.map +1 -1
- package/dist/types/scan/calculateScore.d.ts +4 -1
- package/dist/types/scan/calculateScore.d.ts.map +1 -1
- package/dist/types/scan/checks.d.ts +114 -21
- package/dist/types/scan/checks.d.ts.map +1 -1
- package/dist/types/scan/detection/checkDetails.d.ts +29 -0
- package/dist/types/scan/detection/checkDetails.d.ts.map +1 -0
- package/dist/types/scan/detection/classifyInternalLinks.d.ts +44 -0
- package/dist/types/scan/detection/classifyInternalLinks.d.ts.map +1 -0
- package/dist/types/scan/detection/detectRoutingStrategy.d.ts +60 -0
- package/dist/types/scan/detection/detectRoutingStrategy.d.ts.map +1 -0
- package/dist/types/scan/detection/detectTechnologies.d.ts +60 -0
- package/dist/types/scan/detection/detectTechnologies.d.ts.map +1 -0
- package/dist/types/scan/detection/index.d.ts +9 -0
- package/dist/types/scan/detection/localeCode.d.ts +49 -0
- package/dist/types/scan/detection/localeCode.d.ts.map +1 -0
- package/dist/types/scan/detection/localizedPages.d.ts +40 -0
- package/dist/types/scan/detection/localizedPages.d.ts.map +1 -0
- package/dist/types/scan/detection/technologySignatures.d.ts +48 -0
- package/dist/types/scan/detection/technologySignatures.d.ts.map +1 -0
- package/dist/types/scan/detection/url.d.ts +27 -0
- package/dist/types/scan/detection/url.d.ts.map +1 -0
- package/dist/types/scan/fetchText.d.ts +30 -0
- package/dist/types/scan/fetchText.d.ts.map +1 -0
- package/dist/types/scan/index.d.ts +16 -4
- package/dist/types/scan/parseHtml.d.ts +17 -0
- package/dist/types/scan/parseHtml.d.ts.map +1 -1
- package/dist/types/scan/robots.d.ts +23 -0
- package/dist/types/scan/robots.d.ts.map +1 -0
- package/dist/types/scan/runScanChecks.d.ts +43 -0
- package/dist/types/scan/runScanChecks.d.ts.map +1 -0
- package/dist/types/scan/scanWebsite.d.ts.map +1 -1
- package/dist/types/scan/sitemap.d.ts +49 -0
- package/dist/types/scan/sitemap.d.ts.map +1 -0
- package/dist/types/scan/types.d.ts +15 -0
- package/dist/types/scan/types.d.ts.map +1 -1
- package/dist/types/utils/chunkJSON.d.ts.map +1 -1
- package/dist/types/utils/runParallel/pidTree.d.ts.map +1 -1
- package/dist/types/writeContentDeclaration/writeMarkdownFile.d.ts.map +1 -1
- package/package.json +16 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/backend/index.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport {\n exists,\n readFileFromRoot,\n writeFileToRoot,\n} from '../../utils/fileSystem';\nimport type { FrameworkAdapter } from '../types';\nimport {\n type BackendTransformResult,\n registerElysiaPlugin,\n registerExpressMiddleware,\n registerFastifyPlugin,\n registerHonoMiddleware,\n registerNestJsMiddleware,\n} from './transforms';\n\n/** Script extensions probed for a server entry file. */\nconst ENTRY_EXTENSIONS = ['ts', 'mts', 'js', 'mjs', 'cjs'] as const;\n\n/** Conventional server entry file names, most likely first. */\nconst DEFAULT_ENTRY_BASE_NAMES = [\n 'src/index',\n 'src/server',\n 'src/app',\n 'src/main',\n 'index',\n 'server',\n 'app',\n 'main',\n] as const;\n\n/**\n * Lists the candidate server entry files: the `package.json` `main` field when\n * it points at source (not a build output), then the conventional names.\n */\nconst getEntryCandidates = async (\n rootDir: string,\n entryBaseNames: readonly string[]\n): Promise<string[]> => {\n const candidates: string[] = [];\n\n try {\n const { main } = JSON.parse(\n await readFileFromRoot(rootDir, 'package.json')\n );\n const isSourceEntry =\n typeof main === 'string' && !/^(\\.\\/)?(dist|build|out)\\//.test(main);\n if (isSourceEntry) candidates.push(main.replace(/^\\.\\//, ''));\n } catch {\n // No readable package.json — rely on the conventional names.\n }\n\n for (const baseName of entryBaseNames) {\n for (const extension of ENTRY_EXTENSIONS) {\n candidates.push(`${baseName}.${extension}`);\n }\n }\n\n return [...new Set(candidates)];\n};\n\n/** A server framework whose Intlayer middleware can be registered by init. */\ntype BackendIntegration = {\n /** Human-readable framework name, used in logs. */\n name: string;\n /** Returns true when the project uses this framework. */\n detect: (allDeps: Record<string, string>) => boolean;\n /** Entry file base names to probe, in order. */\n entryBaseNames: readonly string[];\n /** Registers the middleware in an entry file's source. */\n transform: (code: string) => BackendTransformResult;\n /** How the registration reads, shown in logs and manual-setup guidance. */\n registrationHint: string;\n /** Guide for a manual setup. */\n documentationUrl: string;\n};\n\n/**\n * Registers the middleware in the first candidate entry file the transform\n * recognizes. Files the transform cannot confidently edit are left untouched;\n * when none matches, guidance is logged instead.\n */\nconst registerBackendMiddleware = async (\n rootDir: string,\n integration: BackendIntegration\n): Promise<void> => {\n logger(\n colorize(`Setting up ${integration.name} integration...`, ANSIColors.CYAN)\n );\n\n const candidates = await getEntryCandidates(\n rootDir,\n integration.entryBaseNames\n );\n\n for (const candidate of candidates) {\n if (!(await exists(rootDir, candidate))) continue;\n\n const code = await readFileFromRoot(rootDir, candidate);\n\n let result: BackendTransformResult;\n try {\n result = integration.transform(code);\n } catch {\n // Unparsable file — try the next candidate.\n continue;\n }\n\n if (result.status === 'already') {\n logger(\n `${v} ${colorizePath(candidate)} already registers the Intlayer middleware`\n );\n return;\n }\n\n if (result.status === 'registered') {\n await writeFileToRoot(rootDir, candidate, result.code);\n logger(\n `${v} Registered ${colorize(integration.registrationHint, ANSIColors.MAGENTA)} in ${colorizePath(candidate)}`\n );\n return;\n }\n }\n\n logger(\n `${x} Could not find where your ${integration.name} app is created — add ${colorize(integration.registrationHint, ANSIColors.MAGENTA)} manually: ${colorizePath(integration.documentationUrl)}`,\n { level: 'warn' }\n );\n};\n\n/** Wraps a backend integration into a framework adapter. */\nconst createBackendAdapter = (\n integration: BackendIntegration\n): FrameworkAdapter => ({\n name: integration.name,\n detect: ({ allDeps }) => integration.detect(allDeps),\n setup: ({ rootDir }) => registerBackendMiddleware(rootDir, integration),\n});\n\n/**\n * NestJS on the Express platform. Checked before Express: a Nest app is\n * created by `NestFactory`, not `express()`. Nest on Fastify is left to the\n * guide (its adapter is registered through the Fastify instance).\n */\nexport const nestJsAdapter = createBackendAdapter({\n name: 'NestJS',\n detect: (allDeps) =>\n Boolean(allDeps['@nestjs/core']) && !allDeps['@nestjs/platform-fastify'],\n entryBaseNames: ['src/main', 'main'],\n transform: registerNestJsMiddleware,\n registrationHint: 'app.use(intlayer())',\n documentationUrl: 'https://intlayer.org/doc/environment/nest.md',\n});\n\nexport const expressAdapter = createBackendAdapter({\n name: 'Express',\n detect: (allDeps) => Boolean(allDeps.express),\n entryBaseNames: DEFAULT_ENTRY_BASE_NAMES,\n transform: registerExpressMiddleware,\n registrationHint: 'app.use(intlayer())',\n documentationUrl: 'https://intlayer.org/doc/environment/express.md',\n});\n\nexport const fastifyAdapter = createBackendAdapter({\n name: 'Fastify',\n detect: (allDeps) => Boolean(allDeps.fastify),\n entryBaseNames: DEFAULT_ENTRY_BASE_NAMES,\n transform: registerFastifyPlugin,\n registrationHint: 'fastify.register(intlayer)',\n documentationUrl: 'https://intlayer.org/doc/environment/fastify.md',\n});\n\nexport const honoAdapter = createBackendAdapter({\n name: 'Hono',\n detect: (allDeps) => Boolean(allDeps.hono),\n entryBaseNames: DEFAULT_ENTRY_BASE_NAMES,\n transform: registerHonoMiddleware,\n registrationHint: 'app.use(\"*\", intlayer())',\n documentationUrl: 'https://intlayer.org/doc/environment/hono.md',\n});\n\nexport const elysiaAdapter = createBackendAdapter({\n name: 'Elysia',\n detect: (allDeps) => Boolean(allDeps.elysia),\n entryBaseNames: DEFAULT_ENTRY_BASE_NAMES,\n transform: registerElysiaPlugin,\n registrationHint: 'new Elysia().use(intlayer())',\n documentationUrl: 'https://intlayer.org/doc/environment/elysia.md',\n});\n\n/** Backend adapters, in detection order. */\nexport const backendAdapters: FrameworkAdapter[] = [\n nestJsAdapter,\n expressAdapter,\n fastifyAdapter,\n honoAdapter,\n elysiaAdapter,\n];\n"],"mappings":";;;;;;;AAkBA,MAAM,mBAAmB;CAAC;CAAM;CAAO;CAAM;CAAO;AAAK;;AAGzD,MAAM,2BAA2B;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;AAMA,MAAM,qBAAqB,OACzB,SACA,mBACsB;CACtB,MAAM,aAAuB,CAAC;CAE9B,IAAI;EACF,MAAM,EAAE,SAAS,KAAK,MACpB,MAAM,iBAAiB,SAAS,cAAc,CAChD;EAGA,IADE,OAAO,SAAS,YAAY,CAAC,6BAA6B,KAAK,IAAI,GAClD,WAAW,KAAK,KAAK,QAAQ,SAAS,EAAE,CAAC;CAC9D,QAAQ,CAER;CAEA,KAAK,MAAM,YAAY,gBACrB,KAAK,MAAM,aAAa,kBACtB,WAAW,KAAK,GAAG,SAAS,GAAG,WAAW;CAI9C,OAAO,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC;AAChC;;;;;;AAuBA,MAAM,4BAA4B,OAChC,SACA,gBACkB;CAClB,OACE,SAAS,cAAc,YAAY,KAAK,kBAAkB,WAAW,IAAI,CAC3E;CAEA,MAAM,aAAa,MAAM,mBACvB,SACA,YAAY,cACd;CAEA,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,CAAE,MAAM,OAAO,SAAS,SAAS,GAAI;EAEzC,MAAM,OAAO,MAAM,iBAAiB,SAAS,SAAS;EAEtD,IAAI;EACJ,IAAI;GACF,SAAS,YAAY,UAAU,IAAI;EACrC,QAAQ;GAEN;EACF;EAEA,IAAI,OAAO,WAAW,WAAW;GAC/B,OACE,GAAG,EAAE,GAAG,aAAa,SAAS,EAAE,2CAClC;GACA;EACF;EAEA,IAAI,OAAO,WAAW,cAAc;GAClC,MAAM,gBAAgB,SAAS,WAAW,OAAO,IAAI;GACrD,OACE,GAAG,EAAE,cAAc,SAAS,YAAY,kBAAkB,WAAW,OAAO,EAAE,MAAM,aAAa,SAAS,GAC5G;GACA;EACF;CACF;CAEA,OACE,GAAG,EAAE,6BAA6B,YAAY,KAAK,wBAAwB,SAAS,YAAY,kBAAkB,WAAW,OAAO,EAAE,aAAa,aAAa,YAAY,gBAAgB,KAC5L,EAAE,OAAO,OAAO,CAClB;AACF;;AAGA,MAAM,wBACJ,iBACsB;CACtB,MAAM,YAAY;CAClB,SAAS,EAAE,cAAc,YAAY,OAAO,OAAO;CACnD,QAAQ,EAAE,cAAc,0BAA0B,SAAS,WAAW;AACxE;;;;;;AAOA,MAAa,gBAAgB,qBAAqB;CAChD,MAAM;CACN,SAAS,YACP,QAAQ,QAAQ,eAAe,KAAK,CAAC,QAAQ;CAC/C,gBAAgB,CAAC,YAAY,MAAM;CACnC,WAAW;CACX,kBAAkB;CAClB,kBAAkB;AACpB,CAAC;AAED,MAAa,iBAAiB,qBAAqB;CACjD,MAAM;CACN,SAAS,YAAY,QAAQ,QAAQ,OAAO;CAC5C,gBAAgB;CAChB,WAAW;CACX,kBAAkB;CAClB,kBAAkB;AACpB,CAAC;AAED,MAAa,iBAAiB,qBAAqB;CACjD,MAAM;CACN,SAAS,YAAY,QAAQ,QAAQ,OAAO;CAC5C,gBAAgB;CAChB,WAAW;CACX,kBAAkB;CAClB,kBAAkB;AACpB,CAAC;AAED,MAAa,cAAc,qBAAqB;CAC9C,MAAM;CACN,SAAS,YAAY,QAAQ,QAAQ,IAAI;CACzC,gBAAgB;CAChB,WAAW;CACX,kBAAkB;CAClB,kBAAkB;AACpB,CAAC;AAED,MAAa,gBAAgB,qBAAqB;CAChD,MAAM;CACN,SAAS,YAAY,QAAQ,QAAQ,MAAM;CAC3C,gBAAgB;CAChB,WAAW;CACX,kBAAkB;CAClB,kBAAkB;AACpB,CAAC;;AAGD,MAAa,kBAAsC;CACjD;CACA;CACA;CACA;CACA;AACF"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { babelTsParser } from "../../../utils/babelParser.mjs";
|
|
2
|
+
import { ensureNamedImport, isModuleScopeBinding } from "../../utils/astImports.mjs";
|
|
3
|
+
import * as recast from "recast";
|
|
4
|
+
|
|
5
|
+
//#region src/init/frameworkSetup/backend/transforms.ts
|
|
6
|
+
const { namedTypes: n } = recast.types;
|
|
7
|
+
/** Local name the injected middleware / plugin factory is bound to. */
|
|
8
|
+
const MIDDLEWARE_IDENTIFIER = "intlayer";
|
|
9
|
+
/** babel-ts parser handles TypeScript *and* plain JavaScript. */
|
|
10
|
+
const parseCode = (code) => recast.parse(code, { parser: babelTsParser });
|
|
11
|
+
/** Parses a single statement. */
|
|
12
|
+
const parseStatement = (code) => parseCode(code).program.body[0];
|
|
13
|
+
/** Whether `node` is a `require("<source>")` call. */
|
|
14
|
+
const isRequireOf = (node, source) => node?.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "require" && node.arguments[0]?.value === source;
|
|
15
|
+
/**
|
|
16
|
+
* Resolves the local names a factory is bound to at module scope, from ESM
|
|
17
|
+
* imports (`import express from "express"`, `import { Hono } from "hono"`) or
|
|
18
|
+
* CommonJS requires (`const express = require("express")`,
|
|
19
|
+
* `const { Hono } = require("hono")`).
|
|
20
|
+
*/
|
|
21
|
+
const findFactoryLocalNames = (ast, factory) => {
|
|
22
|
+
const localNames = [];
|
|
23
|
+
const acceptsNamed = (name) => factory.named?.includes(name) ?? false;
|
|
24
|
+
for (const statement of ast.program.body) {
|
|
25
|
+
if (statement.type === "ImportDeclaration" && statement.source.value === factory.source) for (const specifier of statement.specifiers) {
|
|
26
|
+
const isAcceptedDefault = specifier.type === "ImportDefaultSpecifier" && factory.default;
|
|
27
|
+
const isAcceptedNamed = specifier.type === "ImportSpecifier" && acceptsNamed(specifier.imported.name);
|
|
28
|
+
if (isAcceptedDefault || isAcceptedNamed) localNames.push(specifier.local.name);
|
|
29
|
+
}
|
|
30
|
+
if (statement.type !== "VariableDeclaration") continue;
|
|
31
|
+
for (const declarator of statement.declarations) {
|
|
32
|
+
if (!isRequireOf(declarator.init, factory.source)) continue;
|
|
33
|
+
if (declarator.id.type === "Identifier" && factory.default) localNames.push(declarator.id.name);
|
|
34
|
+
if (declarator.id.type === "ObjectPattern") {
|
|
35
|
+
for (const property of declarator.id.properties) if (property.key?.type === "Identifier" && property.value?.type === "Identifier" && acceptsNamed(property.key.name)) localNames.push(property.value.name);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return localNames;
|
|
40
|
+
};
|
|
41
|
+
/** Whether the module uses CommonJS (`require`) rather than ESM imports. */
|
|
42
|
+
const isCommonJsModule = (ast) => !ast.program.body.some((statement) => statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration") && ast.program.body.some((statement) => statement.type === "VariableDeclaration" && statement.declarations.some((declarator) => declarator.init?.type === "CallExpression" && declarator.init.callee?.name === "require"));
|
|
43
|
+
/**
|
|
44
|
+
* Binds `intlayer` from `source`: an ESM named import, or — in a CommonJS
|
|
45
|
+
* module — `const { intlayer } = require("<source>");` placed after the last
|
|
46
|
+
* top-level `require`.
|
|
47
|
+
*/
|
|
48
|
+
const ensureMiddlewareBinding = (ast, source) => {
|
|
49
|
+
if (!isCommonJsModule(ast)) {
|
|
50
|
+
ensureNamedImport(ast, MIDDLEWARE_IDENTIFIER, source);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const body = ast.program.body;
|
|
54
|
+
let insertIndex = 0;
|
|
55
|
+
body.forEach((statement, index) => {
|
|
56
|
+
if (statement.type === "VariableDeclaration" && statement.declarations.some((declarator) => declarator.init?.callee?.name === "require")) insertIndex = index + 1;
|
|
57
|
+
});
|
|
58
|
+
body.splice(insertIndex, 0, parseStatement(`const { ${MIDDLEWARE_IDENTIFIER} } = require("${source}");`));
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Finds the first `const <app> = <init>` whose initializer matches, returning
|
|
62
|
+
* the app identifier and the statement path to insert after. `export const`
|
|
63
|
+
* declarations resolve to the export statement.
|
|
64
|
+
*/
|
|
65
|
+
const findAppDeclaration = (ast, matchesInit) => {
|
|
66
|
+
let found = null;
|
|
67
|
+
recast.visit(ast, { visitVariableDeclarator(path) {
|
|
68
|
+
const { id, init } = path.node;
|
|
69
|
+
if (!found && id.type === "Identifier" && init && matchesInit(init)) {
|
|
70
|
+
let statementPath = path.parent;
|
|
71
|
+
if (statementPath.parent?.node.type === "ExportNamedDeclaration") statementPath = statementPath.parent;
|
|
72
|
+
found = {
|
|
73
|
+
appName: id.name,
|
|
74
|
+
statementPath
|
|
75
|
+
};
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
this.traverse(path);
|
|
79
|
+
} });
|
|
80
|
+
return found;
|
|
81
|
+
};
|
|
82
|
+
/** Whether `node` calls one of `calleeNames` directly (`express()`, `new Hono()`). */
|
|
83
|
+
const isFactoryCall = (node, calleeNames, type) => node?.type === type && node.callee?.type === "Identifier" && calleeNames.includes(node.callee.name);
|
|
84
|
+
/**
|
|
85
|
+
* Shared flow of the "declare the app, then register the middleware on the
|
|
86
|
+
* next line" frameworks (Express, Fastify, Hono, NestJS).
|
|
87
|
+
*/
|
|
88
|
+
const registerAfterAppDeclaration = (code, { middlewareSource, findDeclaration, buildRegistration }) => {
|
|
89
|
+
if (code.includes(middlewareSource)) return {
|
|
90
|
+
code,
|
|
91
|
+
status: "already"
|
|
92
|
+
};
|
|
93
|
+
const ast = parseCode(code);
|
|
94
|
+
if (isModuleScopeBinding(ast, MIDDLEWARE_IDENTIFIER)) return {
|
|
95
|
+
code,
|
|
96
|
+
status: "skipped"
|
|
97
|
+
};
|
|
98
|
+
const declaration = findDeclaration(ast);
|
|
99
|
+
if (!declaration) return {
|
|
100
|
+
code,
|
|
101
|
+
status: "skipped"
|
|
102
|
+
};
|
|
103
|
+
declaration.statementPath.insertAfter(parseStatement(buildRegistration(declaration.appName)));
|
|
104
|
+
ensureMiddlewareBinding(ast, middlewareSource);
|
|
105
|
+
return {
|
|
106
|
+
code: recast.print(ast).code,
|
|
107
|
+
status: "registered"
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Registers `express-intlayer` right after `const app = express()`:
|
|
112
|
+
* `app.use(intlayer());`.
|
|
113
|
+
*/
|
|
114
|
+
const registerExpressMiddleware = (code) => registerAfterAppDeclaration(code, {
|
|
115
|
+
middlewareSource: "express-intlayer",
|
|
116
|
+
findDeclaration: (ast) => {
|
|
117
|
+
const factoryNames = findFactoryLocalNames(ast, {
|
|
118
|
+
source: "express",
|
|
119
|
+
default: true
|
|
120
|
+
});
|
|
121
|
+
return findAppDeclaration(ast, (init) => isFactoryCall(init, factoryNames, "CallExpression"));
|
|
122
|
+
},
|
|
123
|
+
buildRegistration: (appName) => `${appName}.use(intlayer());`
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Registers `fastify-intlayer` right after `const app = Fastify()`:
|
|
127
|
+
* `app.register(intlayer);`. Not awaited, so it also works in CommonJS and
|
|
128
|
+
* non-async scopes — Fastify queues plugins until `ready`/`listen`.
|
|
129
|
+
*/
|
|
130
|
+
const registerFastifyPlugin = (code) => registerAfterAppDeclaration(code, {
|
|
131
|
+
middlewareSource: "fastify-intlayer",
|
|
132
|
+
findDeclaration: (ast) => {
|
|
133
|
+
const factoryNames = findFactoryLocalNames(ast, {
|
|
134
|
+
source: "fastify",
|
|
135
|
+
default: true,
|
|
136
|
+
named: ["fastify", "Fastify"]
|
|
137
|
+
});
|
|
138
|
+
return findAppDeclaration(ast, (init) => isFactoryCall(init, factoryNames, "CallExpression"));
|
|
139
|
+
},
|
|
140
|
+
buildRegistration: (appName) => `${appName}.register(intlayer);`
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* Registers `hono-intlayer` right after `const app = new Hono()`:
|
|
144
|
+
* `app.use("*", intlayer());`. A chained declaration
|
|
145
|
+
* (`new Hono().get(…)`) is skipped: its routes would be registered before the
|
|
146
|
+
* middleware, which Hono runs in registration order.
|
|
147
|
+
*/
|
|
148
|
+
const registerHonoMiddleware = (code) => registerAfterAppDeclaration(code, {
|
|
149
|
+
middlewareSource: "hono-intlayer",
|
|
150
|
+
findDeclaration: (ast) => {
|
|
151
|
+
const factoryNames = findFactoryLocalNames(ast, {
|
|
152
|
+
source: "hono",
|
|
153
|
+
named: ["Hono"]
|
|
154
|
+
});
|
|
155
|
+
return findAppDeclaration(ast, (init) => isFactoryCall(init, factoryNames, "NewExpression"));
|
|
156
|
+
},
|
|
157
|
+
buildRegistration: (appName) => `${appName}.use("*", intlayer());`
|
|
158
|
+
});
|
|
159
|
+
/**
|
|
160
|
+
* Registers `express-intlayer` on a NestJS (Express platform) app right after
|
|
161
|
+
* `const app = await NestFactory.create(…)`: `app.use(intlayer());`.
|
|
162
|
+
*/
|
|
163
|
+
const registerNestJsMiddleware = (code) => registerAfterAppDeclaration(code, {
|
|
164
|
+
middlewareSource: "express-intlayer",
|
|
165
|
+
findDeclaration: (ast) => findAppDeclaration(ast, (init) => {
|
|
166
|
+
const call = init.type === "AwaitExpression" ? init.argument : init;
|
|
167
|
+
return n.CallExpression.check(call) && n.MemberExpression.check(call.callee) && call.callee.object.name === "NestFactory" && call.callee.property.name === "create";
|
|
168
|
+
}),
|
|
169
|
+
buildRegistration: (appName) => `${appName}.use(intlayer());`
|
|
170
|
+
});
|
|
171
|
+
/**
|
|
172
|
+
* Registers `elysia-intlayer` directly on the app constructor:
|
|
173
|
+
* `new Elysia()` → `new Elysia().use(intlayer())`. Works for chained
|
|
174
|
+
* declarations too, and keeps the plugin ahead of every route.
|
|
175
|
+
*/
|
|
176
|
+
const registerElysiaPlugin = (code) => {
|
|
177
|
+
const middlewareSource = "elysia-intlayer";
|
|
178
|
+
if (code.includes(middlewareSource)) return {
|
|
179
|
+
code,
|
|
180
|
+
status: "already"
|
|
181
|
+
};
|
|
182
|
+
const ast = parseCode(code);
|
|
183
|
+
if (isModuleScopeBinding(ast, MIDDLEWARE_IDENTIFIER)) return {
|
|
184
|
+
code,
|
|
185
|
+
status: "skipped"
|
|
186
|
+
};
|
|
187
|
+
const factoryNames = findFactoryLocalNames(ast, {
|
|
188
|
+
source: "elysia",
|
|
189
|
+
named: ["Elysia"]
|
|
190
|
+
});
|
|
191
|
+
let registered = false;
|
|
192
|
+
recast.visit(ast, { visitNewExpression(path) {
|
|
193
|
+
if (registered) return false;
|
|
194
|
+
if (isFactoryCall(path.node, factoryNames, "NewExpression")) {
|
|
195
|
+
const withPlugin = parseStatement("__app__.use(intlayer());").expression;
|
|
196
|
+
withPlugin.callee.object = path.node;
|
|
197
|
+
path.replace(withPlugin);
|
|
198
|
+
registered = true;
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
this.traverse(path);
|
|
202
|
+
} });
|
|
203
|
+
if (!registered) return {
|
|
204
|
+
code,
|
|
205
|
+
status: "skipped"
|
|
206
|
+
};
|
|
207
|
+
ensureMiddlewareBinding(ast, middlewareSource);
|
|
208
|
+
return {
|
|
209
|
+
code: recast.print(ast).code,
|
|
210
|
+
status: "registered"
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
//#endregion
|
|
215
|
+
export { registerElysiaPlugin, registerExpressMiddleware, registerFastifyPlugin, registerHonoMiddleware, registerNestJsMiddleware };
|
|
216
|
+
//# sourceMappingURL=transforms.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transforms.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/backend/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\nimport { babelTsParser } from '../../../utils/babelParser';\nimport {\n ensureNamedImport,\n isModuleScopeBinding,\n} from '../../utils/astImports';\n\nconst { namedTypes: n } = recast.types;\n\n/** Local name the injected middleware / plugin factory is bound to. */\nconst MIDDLEWARE_IDENTIFIER = 'intlayer';\n\n/** babel-ts parser handles TypeScript *and* plain JavaScript. */\nconst parseCode = (code: string): any =>\n recast.parse(code, { parser: babelTsParser });\n\n/** Parses a single statement. */\nconst parseStatement = (code: string): any => parseCode(code).program.body[0];\n\n/** Result of a backend transform. `code` is unchanged for any non-`registered` status. */\nexport type BackendTransformResult = {\n code: string;\n status: 'registered' | 'already' | 'skipped';\n};\n\n/** How a server framework exposes its app factory. */\ntype FactoryImport = {\n /** Module the factory is imported from, e.g. `express`. */\n source: string;\n /** Accepted forms: the default export, and/or these named exports. */\n default?: boolean;\n named?: string[];\n};\n\n/** Whether `node` is a `require(\"<source>\")` call. */\nconst isRequireOf = (node: any, source: string): boolean =>\n node?.type === 'CallExpression' &&\n node.callee?.type === 'Identifier' &&\n node.callee.name === 'require' &&\n node.arguments[0]?.value === source;\n\n/**\n * Resolves the local names a factory is bound to at module scope, from ESM\n * imports (`import express from \"express\"`, `import { Hono } from \"hono\"`) or\n * CommonJS requires (`const express = require(\"express\")`,\n * `const { Hono } = require(\"hono\")`).\n */\nconst findFactoryLocalNames = (ast: any, factory: FactoryImport): string[] => {\n const localNames: string[] = [];\n const acceptsNamed = (name: string): boolean =>\n factory.named?.includes(name) ?? false;\n\n for (const statement of ast.program.body) {\n if (\n statement.type === 'ImportDeclaration' &&\n statement.source.value === factory.source\n ) {\n for (const specifier of statement.specifiers) {\n const isAcceptedDefault =\n specifier.type === 'ImportDefaultSpecifier' && factory.default;\n const isAcceptedNamed =\n specifier.type === 'ImportSpecifier' &&\n acceptsNamed(specifier.imported.name);\n if (isAcceptedDefault || isAcceptedNamed) {\n localNames.push(specifier.local.name);\n }\n }\n }\n\n if (statement.type !== 'VariableDeclaration') continue;\n\n for (const declarator of statement.declarations) {\n if (!isRequireOf(declarator.init, factory.source)) continue;\n\n if (declarator.id.type === 'Identifier' && factory.default) {\n localNames.push(declarator.id.name);\n }\n if (declarator.id.type === 'ObjectPattern') {\n for (const property of declarator.id.properties) {\n if (\n property.key?.type === 'Identifier' &&\n property.value?.type === 'Identifier' &&\n acceptsNamed(property.key.name)\n ) {\n localNames.push(property.value.name);\n }\n }\n }\n }\n }\n\n return localNames;\n};\n\n/** Whether the module uses CommonJS (`require`) rather than ESM imports. */\nconst isCommonJsModule = (ast: any): boolean =>\n !ast.program.body.some(\n (statement: any) =>\n statement.type === 'ImportDeclaration' ||\n statement.type === 'ExportNamedDeclaration' ||\n statement.type === 'ExportDefaultDeclaration'\n ) &&\n ast.program.body.some(\n (statement: any) =>\n statement.type === 'VariableDeclaration' &&\n statement.declarations.some(\n (declarator: any) =>\n declarator.init?.type === 'CallExpression' &&\n declarator.init.callee?.name === 'require'\n )\n );\n\n/**\n * Binds `intlayer` from `source`: an ESM named import, or — in a CommonJS\n * module — `const { intlayer } = require(\"<source>\");` placed after the last\n * top-level `require`.\n */\nconst ensureMiddlewareBinding = (ast: any, source: string): void => {\n if (!isCommonJsModule(ast)) {\n ensureNamedImport(ast, MIDDLEWARE_IDENTIFIER, source);\n return;\n }\n\n const body = ast.program.body;\n let insertIndex = 0;\n body.forEach((statement: any, index: number) => {\n const isRequireDeclaration =\n statement.type === 'VariableDeclaration' &&\n statement.declarations.some(\n (declarator: any) => declarator.init?.callee?.name === 'require'\n );\n if (isRequireDeclaration) insertIndex = index + 1;\n });\n\n body.splice(\n insertIndex,\n 0,\n parseStatement(`const { ${MIDDLEWARE_IDENTIFIER} } = require(\"${source}\");`)\n );\n};\n\n/**\n * Finds the first `const <app> = <init>` whose initializer matches, returning\n * the app identifier and the statement path to insert after. `export const`\n * declarations resolve to the export statement.\n */\nconst findAppDeclaration = (\n ast: any,\n matchesInit: (init: any) => boolean\n): { appName: string; statementPath: any } | null => {\n let found: { appName: string; statementPath: any } | null = null;\n\n recast.visit(ast, {\n visitVariableDeclarator(path) {\n const { id, init } = path.node as any;\n if (!found && id.type === 'Identifier' && init && matchesInit(init)) {\n let statementPath = path.parent;\n if (statementPath.parent?.node.type === 'ExportNamedDeclaration') {\n statementPath = statementPath.parent;\n }\n found = { appName: id.name, statementPath };\n return false;\n }\n this.traverse(path);\n },\n });\n\n return found;\n};\n\n/** Whether `node` calls one of `calleeNames` directly (`express()`, `new Hono()`). */\nconst isFactoryCall = (\n node: any,\n calleeNames: string[],\n type: 'CallExpression' | 'NewExpression'\n): boolean =>\n node?.type === type &&\n node.callee?.type === 'Identifier' &&\n calleeNames.includes(node.callee.name);\n\n/**\n * Shared flow of the \"declare the app, then register the middleware on the\n * next line\" frameworks (Express, Fastify, Hono, NestJS).\n */\nconst registerAfterAppDeclaration = (\n code: string,\n {\n middlewareSource,\n findDeclaration,\n buildRegistration,\n }: {\n middlewareSource: string;\n findDeclaration: (ast: any) => ReturnType<typeof findAppDeclaration>;\n buildRegistration: (appName: string) => string;\n }\n): BackendTransformResult => {\n if (code.includes(middlewareSource)) return { code, status: 'already' };\n\n const ast = parseCode(code);\n if (isModuleScopeBinding(ast, MIDDLEWARE_IDENTIFIER)) {\n return { code, status: 'skipped' };\n }\n\n const declaration = findDeclaration(ast);\n if (!declaration) return { code, status: 'skipped' };\n\n declaration.statementPath.insertAfter(\n parseStatement(buildRegistration(declaration.appName))\n );\n ensureMiddlewareBinding(ast, middlewareSource);\n\n return { code: recast.print(ast).code, status: 'registered' };\n};\n\n/**\n * Registers `express-intlayer` right after `const app = express()`:\n * `app.use(intlayer());`.\n */\nexport const registerExpressMiddleware = (\n code: string\n): BackendTransformResult =>\n registerAfterAppDeclaration(code, {\n middlewareSource: 'express-intlayer',\n findDeclaration: (ast) => {\n const factoryNames = findFactoryLocalNames(ast, {\n source: 'express',\n default: true,\n });\n return findAppDeclaration(ast, (init) =>\n isFactoryCall(init, factoryNames, 'CallExpression')\n );\n },\n buildRegistration: (appName) => `${appName}.use(intlayer());`,\n });\n\n/**\n * Registers `fastify-intlayer` right after `const app = Fastify()`:\n * `app.register(intlayer);`. Not awaited, so it also works in CommonJS and\n * non-async scopes — Fastify queues plugins until `ready`/`listen`.\n */\nexport const registerFastifyPlugin = (code: string): BackendTransformResult =>\n registerAfterAppDeclaration(code, {\n middlewareSource: 'fastify-intlayer',\n findDeclaration: (ast) => {\n const factoryNames = findFactoryLocalNames(ast, {\n source: 'fastify',\n default: true,\n named: ['fastify', 'Fastify'],\n });\n return findAppDeclaration(ast, (init) =>\n isFactoryCall(init, factoryNames, 'CallExpression')\n );\n },\n buildRegistration: (appName) => `${appName}.register(intlayer);`,\n });\n\n/**\n * Registers `hono-intlayer` right after `const app = new Hono()`:\n * `app.use(\"*\", intlayer());`. A chained declaration\n * (`new Hono().get(…)`) is skipped: its routes would be registered before the\n * middleware, which Hono runs in registration order.\n */\nexport const registerHonoMiddleware = (code: string): BackendTransformResult =>\n registerAfterAppDeclaration(code, {\n middlewareSource: 'hono-intlayer',\n findDeclaration: (ast) => {\n const factoryNames = findFactoryLocalNames(ast, {\n source: 'hono',\n named: ['Hono'],\n });\n return findAppDeclaration(ast, (init) =>\n isFactoryCall(init, factoryNames, 'NewExpression')\n );\n },\n buildRegistration: (appName) => `${appName}.use(\"*\", intlayer());`,\n });\n\n/**\n * Registers `express-intlayer` on a NestJS (Express platform) app right after\n * `const app = await NestFactory.create(…)`: `app.use(intlayer());`.\n */\nexport const registerNestJsMiddleware = (\n code: string\n): BackendTransformResult =>\n registerAfterAppDeclaration(code, {\n middlewareSource: 'express-intlayer',\n findDeclaration: (ast) =>\n findAppDeclaration(ast, (init) => {\n const call = init.type === 'AwaitExpression' ? init.argument : init;\n return (\n n.CallExpression.check(call) &&\n n.MemberExpression.check(call.callee) &&\n (call.callee.object as any).name === 'NestFactory' &&\n (call.callee.property as any).name === 'create'\n );\n }),\n buildRegistration: (appName) => `${appName}.use(intlayer());`,\n });\n\n/**\n * Registers `elysia-intlayer` directly on the app constructor:\n * `new Elysia()` → `new Elysia().use(intlayer())`. Works for chained\n * declarations too, and keeps the plugin ahead of every route.\n */\nexport const registerElysiaPlugin = (code: string): BackendTransformResult => {\n const middlewareSource = 'elysia-intlayer';\n if (code.includes(middlewareSource)) return { code, status: 'already' };\n\n const ast = parseCode(code);\n if (isModuleScopeBinding(ast, MIDDLEWARE_IDENTIFIER)) {\n return { code, status: 'skipped' };\n }\n\n const factoryNames = findFactoryLocalNames(ast, {\n source: 'elysia',\n named: ['Elysia'],\n });\n\n let registered = false;\n recast.visit(ast, {\n visitNewExpression(path) {\n if (registered) return false;\n if (isFactoryCall(path.node, factoryNames, 'NewExpression')) {\n const withPlugin = parseStatement(\n '__app__.use(intlayer());'\n ).expression;\n withPlugin.callee.object = path.node;\n path.replace(withPlugin);\n registered = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!registered) return { code, status: 'skipped' };\n\n ensureMiddlewareBinding(ast, middlewareSource);\n\n return { code: recast.print(ast).code, status: 'registered' };\n};\n"],"mappings":"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"}
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import { backendAdapters } from "./backend/index.mjs";
|
|
1
2
|
import { nextAppRouterAdapter } from "./nextAppRouter/index.mjs";
|
|
2
3
|
import { tanStackStartAdapter } from "./tanstackStart/index.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/init/frameworkSetup/index.ts
|
|
5
6
|
/**
|
|
6
7
|
* Registered framework adapters, tried in order. The first one whose `detect`
|
|
7
|
-
* returns true handles the project.
|
|
8
|
-
*
|
|
8
|
+
* returns true handles the project. Frontend adapters come first, so an app
|
|
9
|
+
* with its own server keeps its frontend scaffolding. Add new adapters (Nuxt,
|
|
10
|
+
* Vite + React, …) here as they are implemented.
|
|
9
11
|
*/
|
|
10
|
-
const adapters = [
|
|
12
|
+
const adapters = [
|
|
13
|
+
nextAppRouterAdapter,
|
|
14
|
+
tanStackStartAdapter,
|
|
15
|
+
...backendAdapters
|
|
16
|
+
];
|
|
11
17
|
/**
|
|
12
18
|
* Runs framework-specific scaffolding (middleware/proxy, providers in
|
|
13
19
|
* layout/page, example content) for the first adapter that matches the project.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/init/frameworkSetup/index.ts"],"sourcesContent":["import { nextAppRouterAdapter } from './nextAppRouter';\nimport { tanStackStartAdapter } from './tanstackStart';\nimport type { FrameworkAdapter, FrameworkSetupContext } from './types';\n\nexport type { FrameworkAdapter, FrameworkSetupContext } from './types';\n\n/**\n * Registered framework adapters, tried in order. The first one whose `detect`\n * returns true handles the project. Add new adapters (Nuxt
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/init/frameworkSetup/index.ts"],"sourcesContent":["import { backendAdapters } from './backend';\nimport { nextAppRouterAdapter } from './nextAppRouter';\nimport { tanStackStartAdapter } from './tanstackStart';\nimport type { FrameworkAdapter, FrameworkSetupContext } from './types';\n\nexport type { FrameworkAdapter, FrameworkSetupContext } from './types';\n\n/**\n * Registered framework adapters, tried in order. The first one whose `detect`\n * returns true handles the project. Frontend adapters come first, so an app\n * with its own server keeps its frontend scaffolding. Add new adapters (Nuxt,\n * Vite + React, …) here as they are implemented.\n */\nconst adapters: FrameworkAdapter[] = [\n nextAppRouterAdapter,\n tanStackStartAdapter,\n ...backendAdapters,\n];\n\n/**\n * Runs framework-specific scaffolding (middleware/proxy, providers in\n * layout/page, example content) for the first adapter that matches the project.\n * No-ops when no adapter recognizes the project. Each adapter is idempotent and\n * non-destructive, so this is safe to run on existing applications.\n */\nexport const setupFramework = async (\n context: FrameworkSetupContext\n): Promise<void> => {\n for (const adapter of adapters) {\n if (await adapter.detect(context)) {\n await adapter.setup(context);\n return;\n }\n }\n};\n"],"mappings":";;;;;;;;;;;AAaA,MAAM,WAA+B;CACnC;CACA;CACA,GAAG;AACL;;;;;;;AAQA,MAAa,iBAAiB,OAC5B,YACkB;CAClB,KAAK,MAAM,WAAW,UACpB,IAAI,MAAM,QAAQ,OAAO,OAAO,GAAG;EACjC,MAAM,QAAQ,MAAM,OAAO;EAC3B;CACF;AAEJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/init/frameworkSetup/localeRouting.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether the locale is carried by a URL path segment (`/fr/about`), which the
|
|
4
|
+
* framework router must declare (`[locale]`, `{-$locale}`, `$locale`…). Only the
|
|
5
|
+
* prefix routing modes do, and only while the proxy is enabled: `no-prefix` and
|
|
6
|
+
* `search-params` resolve the locale from storage/headers/query instead.
|
|
7
|
+
*/
|
|
8
|
+
const usesLocalePathSegment = ({ routingMode, enableProxy }) => enableProxy && (routingMode === "prefix-all" || routingMode === "prefix-no-default");
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { usesLocalePathSegment };
|
|
12
|
+
//# sourceMappingURL=localeRouting.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localeRouting.mjs","names":[],"sources":["../../../../src/init/frameworkSetup/localeRouting.ts"],"sourcesContent":["import type { FrameworkSetupContext } from './types';\n\n/**\n * Whether the locale is carried by a URL path segment (`/fr/about`), which the\n * framework router must declare (`[locale]`, `{-$locale}`, `$locale`…). Only the\n * prefix routing modes do, and only while the proxy is enabled: `no-prefix` and\n * `search-params` resolve the locale from storage/headers/query instead.\n */\nexport const usesLocalePathSegment = ({\n routingMode,\n enableProxy,\n}: Pick<FrameworkSetupContext, 'routingMode' | 'enableProxy'>): boolean =>\n enableProxy &&\n (routingMode === 'prefix-all' || routingMode === 'prefix-no-default');\n"],"mappings":";;;;;;;AAQA,MAAa,yBAAyB,EACpC,aACA,kBAEA,gBACC,gBAAgB,gBAAgB,gBAAgB"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { ensureDirectory, exists, readFileFromRoot, writeFileToRoot } from "../../utils/fileSystem.mjs";
|
|
2
|
+
import { usesLocalePathSegment } from "../localeRouting.mjs";
|
|
2
3
|
import { isVersionAtLeast } from "../../utils/version.mjs";
|
|
3
4
|
import { detectNextAppDir, findAppFile } from "./detect.mjs";
|
|
4
|
-
import { restructureAppIntoLocale } from "./restructure.mjs";
|
|
5
|
-
import { LOCALE_LAYOUT_TEMPLATE_JS, LOCALE_LAYOUT_TEMPLATE_TS, LOCALE_PAGE_TEMPLATE_JS, LOCALE_PAGE_TEMPLATE_TS, MIDDLEWARE_TEMPLATE, PROXY_TEMPLATE, ROOT_LAYOUT_TEMPLATE_JS, ROOT_LAYOUT_TEMPLATE_TS } from "./templates.mjs";
|
|
5
|
+
import { isLocaleSegment, restructureAppIntoLocale } from "./restructure.mjs";
|
|
6
|
+
import { LOCALE_LAYOUT_TEMPLATE_JS, LOCALE_LAYOUT_TEMPLATE_TS, LOCALE_PAGE_TEMPLATE_JS, LOCALE_PAGE_TEMPLATE_TS, MIDDLEWARE_TEMPLATE, PROXY_TEMPLATE, ROOT_LAYOUT_TEMPLATE_JS, ROOT_LAYOUT_TEMPLATE_TS, UNPREFIXED_PAGE_TEMPLATE, UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS, UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS } from "./templates.mjs";
|
|
6
7
|
import { wrapLayoutWithProvider } 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/nextAppRouter/index.ts
|
|
12
14
|
/**
|
|
@@ -61,11 +63,29 @@ const createIfMissing = async (rootDir, filePath, content, description) => {
|
|
|
61
63
|
return true;
|
|
62
64
|
};
|
|
63
65
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
|
|
66
|
+
* Wires an app whose URLs carry no locale segment (`no-prefix`,
|
|
67
|
+
* `search-params`, or no proxy): routes stay at the app root and the root
|
|
68
|
+
* layout provides the locale resolved by `getLocale()`. An app already nested
|
|
69
|
+
* under a locale segment is left untouched, with guidance.
|
|
70
|
+
*/
|
|
71
|
+
const setupUnprefixedApp = async (rootDir, appDir, useTypeScript) => {
|
|
72
|
+
const scriptExtension = useTypeScript ? "tsx" : "jsx";
|
|
73
|
+
const existingLocaleSegment = (await readdir(join(rootDir, appDir))).find(isLocaleSegment);
|
|
74
|
+
if (existingLocaleSegment) {
|
|
75
|
+
logger(`${x} Routes live under ${colorizePath(join(appDir, existingLocaleSegment))} but the chosen routing has no locale in the URL — move them back to ${colorizePath(appDir)} manually.`, { level: "warn" });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const existingLayout = await findAppFile(rootDir, appDir, "layout");
|
|
79
|
+
if (existingLayout) await transformExistingFile(rootDir, existingLayout, (code) => wrapLayoutWithProvider(code, { exportStaticParams: false }));
|
|
80
|
+
else await createIfMissing(rootDir, join(appDir, `layout.${scriptExtension}`), useTypeScript ? UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS : UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS, "root layout");
|
|
81
|
+
if (!await findAppFile(rootDir, appDir, "page")) await createIfMissing(rootDir, join(appDir, `page.${scriptExtension}`), UNPREFIXED_PAGE_TEMPLATE, "page");
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Next.js App Router adapter. Scaffolds the locale proxy/middleware (when the
|
|
85
|
+
* proxy is enabled), restructures the app into a `[locale]` segment (prefix
|
|
86
|
+
* routing modes only), and wraps the layout with the unified Intlayer provider
|
|
87
|
+
* — all idempotently and without overwriting recognizable user code. Pages are
|
|
88
|
+
* never wrapped: `IntlayerProvider` in the layout covers them.
|
|
69
89
|
*/
|
|
70
90
|
const nextAppRouterAdapter = {
|
|
71
91
|
name: "Next.js App Router",
|
|
@@ -73,14 +93,21 @@ const nextAppRouterAdapter = {
|
|
|
73
93
|
if (!allDeps.next) return false;
|
|
74
94
|
return await detectNextAppDir(rootDir) !== null;
|
|
75
95
|
},
|
|
76
|
-
setup: async ({ rootDir, allDeps, useTypeScript }) => {
|
|
96
|
+
setup: async ({ rootDir, allDeps, useTypeScript, routingMode, enableProxy }) => {
|
|
77
97
|
const appDirInfo = await detectNextAppDir(rootDir);
|
|
78
98
|
if (!appDirInfo) return;
|
|
79
99
|
const { appDir, srcDir } = appDirInfo;
|
|
80
100
|
const isNext16 = isVersionAtLeast(allDeps.next, 16);
|
|
81
101
|
const scriptExtension = useTypeScript ? "tsx" : "jsx";
|
|
82
102
|
logger(colorize("Setting up Next.js App Router integration...", ANSIColors.CYAN));
|
|
83
|
-
await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);
|
|
103
|
+
if (enableProxy) await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);
|
|
104
|
+
if (!usesLocalePathSegment({
|
|
105
|
+
routingMode,
|
|
106
|
+
enableProxy
|
|
107
|
+
})) {
|
|
108
|
+
await setupUnprefixedApp(rootDir, appDir, useTypeScript);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
84
111
|
const restructureResult = await restructureAppIntoLocale(rootDir, appDir);
|
|
85
112
|
if (restructureResult.status === "moved") logger(`${v} Restructured app routes under ${colorizePath(join(appDir, "[locale]"))}: ${restructureResult.movedEntries.map((entry) => colorize(entry, ANSIColors.MAGENTA)).join(", ")}`);
|
|
86
113
|
else if (restructureResult.status === "already-structured") logger(`${v} ${colorizePath(join(appDir, restructureResult.localeSegment))} already exists, skipping restructure`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/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 type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectNextAppDir, findAppFile, isVersionAtLeast } from './detect';\nimport { restructureAppIntoLocale } from './restructure';\nimport {\n LOCALE_LAYOUT_TEMPLATE_JS,\n LOCALE_LAYOUT_TEMPLATE_TS,\n LOCALE_PAGE_TEMPLATE_JS,\n LOCALE_PAGE_TEMPLATE_TS,\n MIDDLEWARE_TEMPLATE,\n PROXY_TEMPLATE,\n ROOT_LAYOUT_TEMPLATE_JS,\n ROOT_LAYOUT_TEMPLATE_TS,\n} from './templates';\nimport { type TransformResult, wrapLayoutWithProvider } from './transforms';\n\n/**\n * Creates the locale-detection proxy (Next >= 16) or middleware (Next < 16),\n * but only when neither already exists, so a user's custom file is never\n * touched. Placed next to the app directory's parent (`src/` when present).\n */\nconst ensureProxyOrMiddleware = async (\n rootDir: string,\n srcDir: string,\n isNext16: boolean\n): Promise<void> => {\n const baseDir = srcDir || '.';\n const proxyCandidates = [\n 'proxy.ts',\n 'proxy.js',\n 'middleware.ts',\n 'middleware.js',\n ];\n\n for (const candidate of proxyCandidates) {\n if (await exists(rootDir, join(baseDir, candidate))) {\n logger(\n `${v} ${colorizePath(join(baseDir, candidate))} already exists, leaving it untouched`\n );\n return;\n }\n }\n\n const fileName = isNext16 ? 'proxy.ts' : 'middleware.ts';\n const content = isNext16 ? PROXY_TEMPLATE : MIDDLEWARE_TEMPLATE;\n const targetPath = join(baseDir, fileName);\n\n if (srcDir) await ensureDirectory(rootDir, srcDir);\n await writeFileToRoot(rootDir, targetPath, content);\n logger(`${v} Created ${colorizePath(targetPath)} for locale detection`);\n};\n\n/** Logs the outcome of a layout 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-client':\n logger(\n `${x} ${colorizePath(filePath)} is a client component — add the Intlayer provider manually.`,\n { level: 'warn' }\n );\n break;\n case 'skipped':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it 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 * Next.js App Router adapter. Scaffolds the locale proxy/middleware, restructures\n * the app into a `[locale]` segment, and wraps the layout with the unified\n * Intlayer provider — all idempotently and without overwriting recognizable\n * user code. Pages are never wrapped: `IntlayerProvider` in the locale layout\n * covers them.\n */\nexport const nextAppRouterAdapter: FrameworkAdapter = {\n name: 'Next.js App Router',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!allDeps.next) return false;\n return (await detectNextAppDir(rootDir)) !== null;\n },\n\n setup: async ({ rootDir, allDeps, useTypeScript }: FrameworkSetupContext) => {\n const appDirInfo = await detectNextAppDir(rootDir);\n if (!appDirInfo) return;\n\n const { appDir, srcDir } = appDirInfo;\n const isNext16 = isVersionAtLeast(allDeps.next, 16);\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n logger(\n colorize('Setting up Next.js App Router integration...', ANSIColors.CYAN)\n );\n\n // 1. Locale detection proxy / middleware (create only when absent).\n await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);\n\n // 2. Move routable files under a `[locale]` segment (idempotent).\n const restructureResult = await restructureAppIntoLocale(rootDir, appDir);\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured app routes under ${colorizePath(join(appDir, '[locale]'))}: ${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(appDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // When the app already uses a different locale-prefix scheme (e.g. an\n // `[...locale]` / `[[...locale]]` catch-all), respect the user's routing:\n // don't scaffold a second, conflicting `[locale]` segment.\n if (\n restructureResult.status === 'already-structured' &&\n restructureResult.localeSegment !== '[locale]'\n ) {\n logger(\n `${v} Detected existing locale routing (${colorize(restructureResult.localeSegment, ANSIColors.MAGENTA)}), skipping locale layout/page scaffold`\n );\n return;\n }\n\n const localeDir = join(appDir, '[locale]');\n await ensureDirectory(rootDir, localeDir);\n\n // 3. Minimal pass-through root layout (the `[locale]` layout owns `<html>`).\n await createIfMissing(\n rootDir,\n join(appDir, `layout.${scriptExtension}`),\n useTypeScript ? ROOT_LAYOUT_TEMPLATE_TS : ROOT_LAYOUT_TEMPLATE_JS,\n 'root layout'\n );\n\n // 4. Locale layout — transform an existing one, else scaffold from template.\n const existingLayout = await findAppFile(rootDir, localeDir, 'layout');\n if (existingLayout) {\n await transformExistingFile(\n rootDir,\n existingLayout,\n wrapLayoutWithProvider\n );\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `layout.${scriptExtension}`),\n useTypeScript ? LOCALE_LAYOUT_TEMPLATE_TS : LOCALE_LAYOUT_TEMPLATE_JS,\n 'locale layout'\n );\n }\n\n // 5. Locale page — only scaffolded when absent. An existing page needs no\n // transform: the locale layout's `IntlayerProvider` already covers it.\n const existingPage = await findAppFile(rootDir, localeDir, 'page');\n if (!existingPage) {\n await createIfMissing(\n rootDir,\n join(localeDir, `page.${scriptExtension}`),\n useTypeScript ? LOCALE_PAGE_TEMPLATE_TS : LOCALE_PAGE_TEMPLATE_JS,\n 'locale page'\n );\n }\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/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 type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectNextAppDir, findAppFile, isVersionAtLeast } from './detect';\nimport { isLocaleSegment, restructureAppIntoLocale } from './restructure';\nimport {\n LOCALE_LAYOUT_TEMPLATE_JS,\n LOCALE_LAYOUT_TEMPLATE_TS,\n LOCALE_PAGE_TEMPLATE_JS,\n LOCALE_PAGE_TEMPLATE_TS,\n MIDDLEWARE_TEMPLATE,\n PROXY_TEMPLATE,\n ROOT_LAYOUT_TEMPLATE_JS,\n ROOT_LAYOUT_TEMPLATE_TS,\n UNPREFIXED_PAGE_TEMPLATE,\n UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS,\n UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS,\n} from './templates';\nimport { type TransformResult, wrapLayoutWithProvider } from './transforms';\n\n/**\n * Creates the locale-detection proxy (Next >= 16) or middleware (Next < 16),\n * but only when neither already exists, so a user's custom file is never\n * touched. Placed next to the app directory's parent (`src/` when present).\n */\nconst ensureProxyOrMiddleware = async (\n rootDir: string,\n srcDir: string,\n isNext16: boolean\n): Promise<void> => {\n const baseDir = srcDir || '.';\n const proxyCandidates = [\n 'proxy.ts',\n 'proxy.js',\n 'middleware.ts',\n 'middleware.js',\n ];\n\n for (const candidate of proxyCandidates) {\n if (await exists(rootDir, join(baseDir, candidate))) {\n logger(\n `${v} ${colorizePath(join(baseDir, candidate))} already exists, leaving it untouched`\n );\n return;\n }\n }\n\n const fileName = isNext16 ? 'proxy.ts' : 'middleware.ts';\n const content = isNext16 ? PROXY_TEMPLATE : MIDDLEWARE_TEMPLATE;\n const targetPath = join(baseDir, fileName);\n\n if (srcDir) await ensureDirectory(rootDir, srcDir);\n await writeFileToRoot(rootDir, targetPath, content);\n logger(`${v} Created ${colorizePath(targetPath)} for locale detection`);\n};\n\n/** Logs the outcome of a layout 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-client':\n logger(\n `${x} ${colorizePath(filePath)} is a client component — add the Intlayer provider manually.`,\n { level: 'warn' }\n );\n break;\n case 'skipped':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it 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 an app whose URLs carry no locale segment (`no-prefix`,\n * `search-params`, or no proxy): routes stay at the app root and the root\n * layout provides the locale resolved by `getLocale()`. An app already nested\n * under a locale segment is left untouched, with guidance.\n */\nconst setupUnprefixedApp = async (\n rootDir: string,\n appDir: string,\n useTypeScript: boolean\n): Promise<void> => {\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n const entries = await readdir(join(rootDir, appDir));\n const existingLocaleSegment = entries.find(isLocaleSegment);\n if (existingLocaleSegment) {\n logger(\n `${x} Routes live under ${colorizePath(join(appDir, existingLocaleSegment))} but the chosen routing has no locale in the URL — move them back to ${colorizePath(appDir)} manually.`,\n { level: 'warn' }\n );\n return;\n }\n\n const existingLayout = await findAppFile(rootDir, appDir, 'layout');\n if (existingLayout) {\n await transformExistingFile(rootDir, existingLayout, (code) =>\n wrapLayoutWithProvider(code, { exportStaticParams: false })\n );\n } else {\n await createIfMissing(\n rootDir,\n join(appDir, `layout.${scriptExtension}`),\n useTypeScript\n ? UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS\n : UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS,\n 'root layout'\n );\n }\n\n if (!(await findAppFile(rootDir, appDir, 'page'))) {\n await createIfMissing(\n rootDir,\n join(appDir, `page.${scriptExtension}`),\n UNPREFIXED_PAGE_TEMPLATE,\n 'page'\n );\n }\n};\n\n/**\n * Next.js App Router adapter. Scaffolds the locale proxy/middleware (when the\n * proxy is enabled), restructures the app into a `[locale]` segment (prefix\n * routing modes only), and wraps the layout with the unified Intlayer provider\n * — all idempotently and without overwriting recognizable user code. Pages are\n * never wrapped: `IntlayerProvider` in the layout covers them.\n */\nexport const nextAppRouterAdapter: FrameworkAdapter = {\n name: 'Next.js App Router',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!allDeps.next) return false;\n return (await detectNextAppDir(rootDir)) !== null;\n },\n\n setup: async ({\n rootDir,\n allDeps,\n useTypeScript,\n routingMode,\n enableProxy,\n }: FrameworkSetupContext) => {\n const appDirInfo = await detectNextAppDir(rootDir);\n if (!appDirInfo) return;\n\n const { appDir, srcDir } = appDirInfo;\n const isNext16 = isVersionAtLeast(allDeps.next, 16);\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n logger(\n colorize('Setting up Next.js App Router integration...', ANSIColors.CYAN)\n );\n\n // 1. Locale detection proxy / middleware (create only when absent).\n if (enableProxy) {\n await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);\n }\n\n // Without a locale path segment, routes stay where they are.\n if (!usesLocalePathSegment({ routingMode, enableProxy })) {\n await setupUnprefixedApp(rootDir, appDir, useTypeScript);\n return;\n }\n\n // 2. Move routable files under a `[locale]` segment (idempotent).\n const restructureResult = await restructureAppIntoLocale(rootDir, appDir);\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured app routes under ${colorizePath(join(appDir, '[locale]'))}: ${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(appDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // When the app already uses a different locale-prefix scheme (e.g. an\n // `[...locale]` / `[[...locale]]` catch-all), respect the user's routing:\n // don't scaffold a second, conflicting `[locale]` segment.\n if (\n restructureResult.status === 'already-structured' &&\n restructureResult.localeSegment !== '[locale]'\n ) {\n logger(\n `${v} Detected existing locale routing (${colorize(restructureResult.localeSegment, ANSIColors.MAGENTA)}), skipping locale layout/page scaffold`\n );\n return;\n }\n\n const localeDir = join(appDir, '[locale]');\n await ensureDirectory(rootDir, localeDir);\n\n // 3. Minimal pass-through root layout (the `[locale]` layout owns `<html>`).\n await createIfMissing(\n rootDir,\n join(appDir, `layout.${scriptExtension}`),\n useTypeScript ? ROOT_LAYOUT_TEMPLATE_TS : ROOT_LAYOUT_TEMPLATE_JS,\n 'root layout'\n );\n\n // 4. Locale layout — transform an existing one, else scaffold from template.\n const existingLayout = await findAppFile(rootDir, localeDir, 'layout');\n if (existingLayout) {\n await transformExistingFile(\n rootDir,\n existingLayout,\n wrapLayoutWithProvider\n );\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `layout.${scriptExtension}`),\n useTypeScript ? LOCALE_LAYOUT_TEMPLATE_TS : LOCALE_LAYOUT_TEMPLATE_JS,\n 'locale layout'\n );\n }\n\n // 5. Locale page — only scaffolded when absent. An existing page needs no\n // transform: the locale layout's `IntlayerProvider` already covers it.\n const existingPage = await findAppFile(rootDir, localeDir, 'page');\n if (!existingPage) {\n await createIfMissing(\n rootDir,\n join(localeDir, `page.${scriptExtension}`),\n useTypeScript ? LOCALE_PAGE_TEMPLATE_TS : LOCALE_PAGE_TEMPLATE_JS,\n 'locale page'\n );\n }\n },\n};\n"],"mappings":"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"}
|
|
@@ -107,9 +107,62 @@ const LOCALE_PAGE_TEMPLATE_JS = `const Page = async ({ params }) => {
|
|
|
107
107
|
);
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
+
export default Page;
|
|
111
|
+
`;
|
|
112
|
+
/**
|
|
113
|
+
* Root layout (`app/layout.tsx`) for routing modes without a locale path
|
|
114
|
+
* segment (`no-prefix`, `search-params`, or no proxy at all). Owns `<html>` and
|
|
115
|
+
* resolves the locale from the request (cookie / header) via `getLocale()`.
|
|
116
|
+
*/
|
|
117
|
+
const UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS = `import type { PropsWithChildren } from "react";
|
|
118
|
+
import { getHTMLTextDir } from "intlayer";
|
|
119
|
+
import { getLocale, IntlayerProvider } from "next-intlayer/server";
|
|
120
|
+
|
|
121
|
+
const RootLayout = async ({ children }: PropsWithChildren) => {
|
|
122
|
+
const locale = await getLocale();
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
126
|
+
<body>
|
|
127
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
128
|
+
</body>
|
|
129
|
+
</html>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export default RootLayout;
|
|
134
|
+
`;
|
|
135
|
+
const UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS = `import { getHTMLTextDir } from "intlayer";
|
|
136
|
+
import { getLocale, IntlayerProvider } from "next-intlayer/server";
|
|
137
|
+
|
|
138
|
+
const RootLayout = async ({ children }) => {
|
|
139
|
+
const locale = await getLocale();
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<html lang={locale} dir={getHTMLTextDir(locale)}>
|
|
143
|
+
<body>
|
|
144
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
145
|
+
</body>
|
|
146
|
+
</html>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default RootLayout;
|
|
151
|
+
`;
|
|
152
|
+
/**
|
|
153
|
+
* Home page (`app/page.tsx`) for routing modes without a locale path segment.
|
|
154
|
+
* Identical for TypeScript and JavaScript projects.
|
|
155
|
+
*/
|
|
156
|
+
const UNPREFIXED_PAGE_TEMPLATE = `const Page = () => (
|
|
157
|
+
<main>
|
|
158
|
+
<h1>Hello</h1>
|
|
159
|
+
<p>Get started by editing this page.</p>
|
|
160
|
+
</main>
|
|
161
|
+
);
|
|
162
|
+
|
|
110
163
|
export default Page;
|
|
111
164
|
`;
|
|
112
165
|
|
|
113
166
|
//#endregion
|
|
114
|
-
export { LOCALE_LAYOUT_TEMPLATE_JS, LOCALE_LAYOUT_TEMPLATE_TS, LOCALE_PAGE_TEMPLATE_JS, LOCALE_PAGE_TEMPLATE_TS, MIDDLEWARE_TEMPLATE, PROXY_TEMPLATE, ROOT_LAYOUT_TEMPLATE_JS, ROOT_LAYOUT_TEMPLATE_TS };
|
|
167
|
+
export { LOCALE_LAYOUT_TEMPLATE_JS, LOCALE_LAYOUT_TEMPLATE_TS, LOCALE_PAGE_TEMPLATE_JS, LOCALE_PAGE_TEMPLATE_TS, MIDDLEWARE_TEMPLATE, PROXY_TEMPLATE, ROOT_LAYOUT_TEMPLATE_JS, ROOT_LAYOUT_TEMPLATE_TS, UNPREFIXED_PAGE_TEMPLATE, UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS, UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS };
|
|
115
168
|
//# sourceMappingURL=templates.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh Next.js App Router integration. These are only\n * written when the target file does not exist (or, for the locale layout/page,\n * when the app is effectively empty) — they never overwrite user code.\n */\n\n/** Next.js >= 16 locale-detection proxy (`src/proxy.ts`). */\nexport const PROXY_TEMPLATE = `export { intlayerProxy as proxy } from \"next-intlayer/proxy\";\n\nexport const config = {\n matcher:\n \"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\\\\\\\..*|_next).*)\",\n};\n`;\n\n/** Next.js < 16 locale-detection middleware (`src/middleware.ts`). */\nexport const MIDDLEWARE_TEMPLATE = `export { intlayerMiddleware as middleware } from \"next-intlayer/middleware\";\n\nexport const config = {\n matcher:\n \"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\\\\\\\..*|_next).*)\",\n};\n`;\n\n/** Minimal pass-through root layout (`app/layout.tsx`), lets `[locale]/layout` own `<html>`. */\nexport const ROOT_LAYOUT_TEMPLATE_TS = `import type { PropsWithChildren, FC } from \"react\";\n\nconst RootLayout: FC<PropsWithChildren> = ({ children }) => <>{children}</>;\n\nexport default RootLayout;\n`;\n\nexport const ROOT_LAYOUT_TEMPLATE_JS = `const RootLayout = ({ children }) => <>{children}</>;\n\nexport default RootLayout;\n`;\n\n/**\n * Locale layout (`app/[locale]/layout.tsx`) providing `<html>` + the unified\n * provider. `IntlayerProvider` seeds both the server context (read by server\n * hooks and by any page below, without per-page wrapping) and the client\n * provider in one mount.\n */\nexport const LOCALE_LAYOUT_TEMPLATE_TS = `import { type NextLayoutIntlayer } from \"next-intlayer\";\nimport { IntlayerProvider } from \"next-intlayer/server\";\nimport { getHTMLTextDir } from \"intlayer\";\n\nexport { generateStaticParams } from \"next-intlayer\";\n\nconst LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {\n const { locale } = await params;\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default LocaleLayout;\n`;\n\nexport const LOCALE_LAYOUT_TEMPLATE_JS = `import { IntlayerProvider } from \"next-intlayer/server\";\nimport { getHTMLTextDir } from \"intlayer\";\n\nexport { generateStaticParams } from \"next-intlayer\";\n\nconst LocaleLayout = async ({ children, params }) => {\n const { locale } = await params;\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default LocaleLayout;\n`;\n\n/**\n * Locale home page (`app/[locale]/page.tsx`). Self-contained starter that does\n * not depend on an example content declaration — add Intlayer content (a\n * `*.content.ts` file consumed via `useIntlayer`) as the next step.\n */\nexport const LOCALE_PAGE_TEMPLATE_TS = `import { type NextPageIntlayer } from \"next-intlayer\";\n\nconst Page: NextPageIntlayer = async ({ params }) => {\n const { locale } = await params;\n\n return (\n <main>\n <h1>{locale}</h1>\n <p>Get started by editing this page.</p>\n </main>\n );\n};\n\nexport default Page;\n`;\n\nexport const LOCALE_PAGE_TEMPLATE_JS = `const Page = async ({ params }) => {\n const { locale } = await params;\n\n return (\n <main>\n <h1>{locale}</h1>\n <p>Get started by editing this page.</p>\n </main>\n );\n};\n\nexport default Page;\n`;\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;;;;;;;;AAS9B,MAAa,sBAAsB;;;;;;;;AASnC,MAAa,0BAA0B;;;;;;AAOvC,MAAa,0BAA0B;;;;;;;;;;AAWvC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;AAqBzC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;AAyBzC,MAAa,0BAA0B;;;;;;;;;;;;;;;AAgBvC,MAAa,0BAA0B"}
|
|
1
|
+
{"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh Next.js App Router integration. These are only\n * written when the target file does not exist (or, for the locale layout/page,\n * when the app is effectively empty) — they never overwrite user code.\n */\n\n/** Next.js >= 16 locale-detection proxy (`src/proxy.ts`). */\nexport const PROXY_TEMPLATE = `export { intlayerProxy as proxy } from \"next-intlayer/proxy\";\n\nexport const config = {\n matcher:\n \"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\\\\\\\..*|_next).*)\",\n};\n`;\n\n/** Next.js < 16 locale-detection middleware (`src/middleware.ts`). */\nexport const MIDDLEWARE_TEMPLATE = `export { intlayerMiddleware as middleware } from \"next-intlayer/middleware\";\n\nexport const config = {\n matcher:\n \"/((?!api|static|assets|robots|sitemap|sw|service-worker|manifest|.*\\\\\\\\..*|_next).*)\",\n};\n`;\n\n/** Minimal pass-through root layout (`app/layout.tsx`), lets `[locale]/layout` own `<html>`. */\nexport const ROOT_LAYOUT_TEMPLATE_TS = `import type { PropsWithChildren, FC } from \"react\";\n\nconst RootLayout: FC<PropsWithChildren> = ({ children }) => <>{children}</>;\n\nexport default RootLayout;\n`;\n\nexport const ROOT_LAYOUT_TEMPLATE_JS = `const RootLayout = ({ children }) => <>{children}</>;\n\nexport default RootLayout;\n`;\n\n/**\n * Locale layout (`app/[locale]/layout.tsx`) providing `<html>` + the unified\n * provider. `IntlayerProvider` seeds both the server context (read by server\n * hooks and by any page below, without per-page wrapping) and the client\n * provider in one mount.\n */\nexport const LOCALE_LAYOUT_TEMPLATE_TS = `import { type NextLayoutIntlayer } from \"next-intlayer\";\nimport { IntlayerProvider } from \"next-intlayer/server\";\nimport { getHTMLTextDir } from \"intlayer\";\n\nexport { generateStaticParams } from \"next-intlayer\";\n\nconst LocaleLayout: NextLayoutIntlayer = async ({ children, params }) => {\n const { locale } = await params;\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default LocaleLayout;\n`;\n\nexport const LOCALE_LAYOUT_TEMPLATE_JS = `import { IntlayerProvider } from \"next-intlayer/server\";\nimport { getHTMLTextDir } from \"intlayer\";\n\nexport { generateStaticParams } from \"next-intlayer\";\n\nconst LocaleLayout = async ({ children, params }) => {\n const { locale } = await params;\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default LocaleLayout;\n`;\n\n/**\n * Locale home page (`app/[locale]/page.tsx`). Self-contained starter that does\n * not depend on an example content declaration — add Intlayer content (a\n * `*.content.ts` file consumed via `useIntlayer`) as the next step.\n */\nexport const LOCALE_PAGE_TEMPLATE_TS = `import { type NextPageIntlayer } from \"next-intlayer\";\n\nconst Page: NextPageIntlayer = async ({ params }) => {\n const { locale } = await params;\n\n return (\n <main>\n <h1>{locale}</h1>\n <p>Get started by editing this page.</p>\n </main>\n );\n};\n\nexport default Page;\n`;\n\nexport const LOCALE_PAGE_TEMPLATE_JS = `const Page = async ({ params }) => {\n const { locale } = await params;\n\n return (\n <main>\n <h1>{locale}</h1>\n <p>Get started by editing this page.</p>\n </main>\n );\n};\n\nexport default Page;\n`;\n\n/**\n * Root layout (`app/layout.tsx`) for routing modes without a locale path\n * segment (`no-prefix`, `search-params`, or no proxy at all). Owns `<html>` and\n * resolves the locale from the request (cookie / header) via `getLocale()`.\n */\nexport const UNPREFIXED_ROOT_LAYOUT_TEMPLATE_TS = `import type { PropsWithChildren } from \"react\";\nimport { getHTMLTextDir } from \"intlayer\";\nimport { getLocale, IntlayerProvider } from \"next-intlayer/server\";\n\nconst RootLayout = async ({ children }: PropsWithChildren) => {\n const locale = await getLocale();\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default RootLayout;\n`;\n\nexport const UNPREFIXED_ROOT_LAYOUT_TEMPLATE_JS = `import { getHTMLTextDir } from \"intlayer\";\nimport { getLocale, IntlayerProvider } from \"next-intlayer/server\";\n\nconst RootLayout = async ({ children }) => {\n const locale = await getLocale();\n\n return (\n <html lang={locale} dir={getHTMLTextDir(locale)}>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n </body>\n </html>\n );\n};\n\nexport default RootLayout;\n`;\n\n/**\n * Home page (`app/page.tsx`) for routing modes without a locale path segment.\n * Identical for TypeScript and JavaScript projects.\n */\nexport const UNPREFIXED_PAGE_TEMPLATE = `const Page = () => (\n <main>\n <h1>Hello</h1>\n <p>Get started by editing this page.</p>\n </main>\n);\n\nexport default Page;\n`;\n"],"mappings":";;;;;;;AAOA,MAAa,iBAAiB;;;;;;;;AAS9B,MAAa,sBAAsB;;;;;;;;AASnC,MAAa,0BAA0B;;;;;;AAOvC,MAAa,0BAA0B;;;;;;;;;;AAWvC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;AAqBzC,MAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;AAyBzC,MAAa,0BAA0B;;;;;;;;;;;;;;;AAgBvC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;AAmBvC,MAAa,qCAAqC;;;;;;;;;;;;;;;;;;AAmBlD,MAAa,qCAAqC;;;;;;;;;;;;;;;;;;;;;AAsBlD,MAAa,2BAA2B"}
|
|
@@ -71,7 +71,10 @@ const setHtmlLang = (ast) => {
|
|
|
71
71
|
const langAttr = node.attributes?.find((attr) => attr.type === "JSXAttribute" && attr.name?.name === "lang");
|
|
72
72
|
const localeExpression = b.jsxExpressionContainer(b.identifier("locale"));
|
|
73
73
|
if (langAttr) langAttr.value = localeExpression;
|
|
74
|
-
else
|
|
74
|
+
else {
|
|
75
|
+
node.attributes = node.attributes ?? [];
|
|
76
|
+
node.attributes.push(b.jsxAttribute(b.jsxIdentifier("lang"), localeExpression));
|
|
77
|
+
}
|
|
75
78
|
return false;
|
|
76
79
|
}
|
|
77
80
|
this.traverse(path);
|
|
@@ -88,7 +91,7 @@ const setHtmlLang = (ast) => {
|
|
|
88
91
|
* pages below the locale layout read locale/variant from it without wrapping
|
|
89
92
|
* themselves individually (see the removed `wrapPageWithProvider`).
|
|
90
93
|
*/
|
|
91
|
-
const wrapLayoutWithProvider = (code) => {
|
|
94
|
+
const wrapLayoutWithProvider = (code, { exportStaticParams = true } = {}) => {
|
|
92
95
|
const ast = parseTsx(code);
|
|
93
96
|
if (isClientComponent(ast)) return {
|
|
94
97
|
code,
|
|
@@ -120,7 +123,7 @@ const wrapLayoutWithProvider = (code) => {
|
|
|
120
123
|
};
|
|
121
124
|
ensureNamedImport(ast, "IntlayerProvider", "next-intlayer/server");
|
|
122
125
|
ensureNamedImport(ast, "getLocale", "next-intlayer/server");
|
|
123
|
-
ensureExportFrom(ast, "generateStaticParams", "next-intlayer");
|
|
126
|
+
if (exportStaticParams) ensureExportFrom(ast, "generateStaticParams", "next-intlayer");
|
|
124
127
|
ensureAwaitedLocale(funcNode);
|
|
125
128
|
setHtmlLang(ast);
|
|
126
129
|
return {
|