everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 8.6 kB
Source Map (JSON)
{"version":3,"file":"i18n-seam.cjs","names":["Effect","fileExists","readText","Finding"],"sources":["../../../../../src/services/scaffold/upgrade-audit/checks/i18n-seam.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { Finding } from \"../domain\";\nimport { fileExists, readText } from \"../fs\";\n\nexport const APP_PATH = \"ui/src/app.ts\";\nexport const CATALOGS_PATH = \"ui/src/i18n/catalogs.ts\";\nexport const RUNTIME_PATH = \"ui/src/i18n/runtime.tsx\";\nexport const ROOT_PATH = \"ui/src/routes/__root.tsx\";\nexport const RUNTIME_IMPORT = \"@/i18n/runtime\";\n\nexport interface AppLocaleConfig {\n readonly locales: readonly string[];\n readonly defaultLocale: string;\n readonly cookieName: string;\n}\n\nexport type I18nSeam = \"wired\" | \"unmounted\" | \"missing\" | \"absent-root\";\n\nexport interface I18nSeamState {\n readonly appLocale: AppLocaleConfig | null;\n readonly seam: I18nSeam;\n readonly catalogsDrift: readonly string[];\n}\n\nexport const parseAppLocale = (source: string): AppLocaleConfig | null => {\n const match = source.match(/export\\s+const\\s+appLocale[^=]*=\\s*\\{([\\s\\S]*?)\\}/);\n if (!match) return null;\n const body = match[1];\n const locales = body.match(/locales:\\s*\\[([^\\]]*)\\]/)?.[1];\n const defaultLocale = body.match(/defaultLocale:\\s*\"([^\"]+)\"/)?.[1];\n const cookieName = body.match(/cookieName:\\s*\"([^\"]+)\"/)?.[1];\n if (!locales || !defaultLocale || !cookieName) return null;\n return {\n locales: locales\n .split(\",\")\n .map((l) => l.trim().replace(/^[\"']|[\"']$/g, \"\"))\n .filter(Boolean),\n defaultLocale,\n cookieName,\n };\n};\n\nconst parseCatalogs = (source: string): AppLocaleConfig | null => {\n const locales = source.match(/APP_LOCALES\\s*=\\s*\\[([^\\]]*)\\]/)?.[1];\n const defaultLocale = source.match(/DEFAULT_APP_LOCALE[^=]*=\\s*\"([^\"]+)\"/)?.[1];\n const cookieName = source.match(/APP_LOCALE_COOKIE[^=]*=\\s*\"([^\"]+)\"/)?.[1];\n if (!locales || !defaultLocale || !cookieName) return null;\n return {\n locales: locales\n .split(\",\")\n .map((l) => l.trim().replace(/^[\"']|[\"']$/g, \"\"))\n .filter(Boolean),\n defaultLocale,\n cookieName,\n };\n};\n\nconst localeDrift = (appLocale: AppLocaleConfig, catalogs: AppLocaleConfig): string[] => {\n const drift: string[] = [];\n if (appLocale.defaultLocale !== catalogs.defaultLocale)\n drift.push(\n `defaultLocale: app.ts \"${appLocale.defaultLocale}\" vs catalogs \"${catalogs.defaultLocale}\"`,\n );\n if (appLocale.cookieName !== catalogs.cookieName)\n drift.push(`cookieName: app.ts \"${appLocale.cookieName}\" vs catalogs \"${catalogs.cookieName}\"`);\n const a = appLocale.locales.join(\",\");\n const b = catalogs.locales.join(\",\");\n if (a !== b) drift.push(`locales: app.ts [${a}] vs catalogs [${b}]`);\n return drift;\n};\n\nexport const readI18nSeam = (projectDir: string): Effect.Effect<I18nSeamState, Error> =>\n Effect.gen(function* () {\n const appExists = yield* fileExists(projectDir, APP_PATH);\n const appLocale = appExists ? parseAppLocale(yield* readText(projectDir, APP_PATH)) : null;\n\n const runtimeExists = yield* fileExists(projectDir, RUNTIME_PATH);\n const rootExists = yield* fileExists(projectDir, ROOT_PATH);\n\n let seam: I18nSeam;\n if (!runtimeExists) seam = \"missing\";\n else if (!rootExists) seam = \"absent-root\";\n else {\n const rootSource = yield* readText(projectDir, ROOT_PATH);\n seam = rootSource.includes(RUNTIME_IMPORT) ? \"wired\" : \"unmounted\";\n }\n\n let catalogsDrift: string[] = [];\n if (appLocale && (yield* fileExists(projectDir, CATALOGS_PATH))) {\n const catalogs = parseCatalogs(yield* readText(projectDir, CATALOGS_PATH));\n if (catalogs) catalogsDrift = localeDrift(appLocale, catalogs);\n }\n\n return { appLocale, seam, catalogsDrift };\n });\n\nexport const checkI18nSeam = (projectDir: string): Effect.Effect<Finding[], Error> =>\n Effect.gen(function* () {\n const { appLocale, seam, catalogsDrift } = yield* readI18nSeam(projectDir);\n const findings: Finding[] = [];\n\n // The host's auth plugin consumes shared @lingui/* (import: false) — only a\n // wired core-ui provides it, so the seam matters even without appLocale.\n // A seam that ships no @lingui/* into the share scope is blocker-class:\n // SSR composition dies with RUNTIME-012 and the app cannot boot. Catalog\n // drift is warn-class — cosmetic config skew, not composition-fatal.\n if (seam === \"missing\") {\n findings.push(\n new Finding({\n check: \"i18n-seam\",\n path: RUNTIME_PATH,\n severity: \"blocker\",\n message:\n \"the shared i18n runtime is not wired — the UI bundle shares no @lingui/*, so the host's auth plugin fails SSR composition with RUNTIME-012 (shareKey @lingui/core). Run `pnpm i18n-seam` to scaffold ui/src/i18n and mount AppI18nProvider\",\n }),\n );\n } else if (seam === \"unmounted\") {\n findings.push(\n new Finding({\n check: \"i18n-seam\",\n path: RUNTIME_PATH,\n severity: \"blocker\",\n message:\n \"ui/src/i18n/runtime.tsx exists but no route imports it — the provider never enters the bundle, so @lingui/* stays out of the share scope (RUNTIME-012 at SSR composition). Mount AppI18nProvider in the root route\",\n }),\n );\n } else if (seam === \"absent-root\") {\n findings.push(\n new Finding({\n check: \"i18n-seam\",\n path: ROOT_PATH,\n severity: \"blocker\",\n message:\n \"ui/src/i18n/runtime.tsx exists but ui/src/routes/__root.tsx is absent — the provider cannot be mounted and the bundle shares no @lingui/* (RUNTIME-012 at SSR composition); inspect the route tree\",\n }),\n );\n }\n\n if (appLocale && catalogsDrift.length > 0) {\n findings.push(\n new Finding({\n check: \"i18n-seam\",\n path: CATALOGS_PATH,\n severity: \"warn\",\n message: `i18n catalogs drift from app.ts's appLocale: ${catalogsDrift.join(\"; \")}`,\n }),\n );\n }\n\n return findings;\n });\n"],"mappings":"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"}