UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

1 lines • 6.48 kB
{"version":3,"file":"i18n-seam.mjs","names":[],"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 if (seam === \"missing\") {\n findings.push(\n new Finding({\n check: \"i18n-seam\",\n path: RUNTIME_PATH,\n severity: \"warn\",\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: \"warn\",\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: \"warn\",\n message:\n \"ui/src/i18n/runtime.tsx exists but ui/src/routes/__root.tsx is absent — the provider cannot be mounted; 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":";;;AAIA,MAAa,WAAW;AACxB,MAAa,gBAAgB;AAC7B,MAAa,eAAe;AAC5B,MAAa,YAAY;AACzB,MAAa,iBAAiB;AAgB9B,MAAa,kBAAkB,WAA2C;CACxE,MAAM,QAAQ,OAAO,MAAM,mDAAmD;CAC9E,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,OAAO,MAAM;CACnB,MAAM,UAAU,KAAK,MAAM,yBAAyB,CAAC,GAAG;CACxD,MAAM,gBAAgB,KAAK,MAAM,4BAA4B,CAAC,GAAG;CACjE,MAAM,aAAa,KAAK,MAAM,yBAAyB,CAAC,GAAG;CAC3D,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,YAAY,OAAO;CACtD,OAAO;EACL,SAAS,QACN,MAAM,GAAG,CAAC,CACV,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,gBAAgB,EAAE,CAAC,CAAC,CAChD,OAAO,OAAO;EACjB;EACA;CACF;AACF"}