fix(admin): use import.meta.glob for locale catalog resolution#499
fix(admin): use import.meta.glob for locale catalog resolution#499
Conversation
The dynamic import in admin.astro used a template literal to load locale catalogs cross-package, which failed in non-monorepo installs because Vite's SSR module runner couldn't resolve the path. The Vite alias entry intended to fix this used `*` in the find string, which is treated as a literal character, not a wildcard. Moves locale loading into the admin package via `loadMessages()` using `import.meta.glob`, so Vite resolves catalogs at build time with reliable relative paths. Removes the no-op Vite alias.
🦋 Changeset detectedLatest commit: 4af062a The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 4af062a | Apr 12 2026, 09:04 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Fixes admin SSR locale catalog resolution when emdash / @emdash-cms/admin are consumed from npm (non-monorepo) by moving locale catalog loading behind a stable API in the admin package.
Changes:
- Replace cross-package dynamic
import()of locale catalogs inadmin.astrowithloadMessages()from@emdash-cms/admin/locales. - Remove the ineffective Vite alias that attempted to match
@emdash-cms/admin/locales/*. - Add
loadMessages()implementation usingimport.meta.glob, plus a small test and a changeset.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/core/src/astro/routes/admin.astro | Switches admin SSR route to call loadMessages() instead of dynamic importing catalogs via a template literal. |
| packages/core/src/astro/integration/vite-config.ts | Removes the prior *-based locale alias that didn’t work in Vite. |
| packages/admin/src/locales/index.ts | Introduces loadMessages() backed by import.meta.glob for build-time catalog discovery. |
| packages/admin/tests/lib/locales.test.ts | Adds tests asserting catalogs load for supported locales and fallback behavior. |
| .changeset/rich-drinks-hug.md | Publishes patch releases for emdash and @emdash-cms/admin documenting the fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| resolve: { | ||
| dedupe: ["@emdash-cms/admin", "react", "react-dom"], | ||
| // Array form so more-specific entries are checked first. | ||
| // The styles.css alias must come before the package alias, otherwise | ||
| // Vite's prefix matching on "@emdash-cms/admin" would resolve | ||
| // "@emdash-cms/admin/styles.css" through the source directory. | ||
| alias: [ | ||
| { find: "@emdash-cms/admin/styles.css", replacement: resolve(adminDistPath, "styles.css") }, | ||
| { | ||
| find: "@emdash-cms/admin/locales/*", | ||
| replacement: resolve(adminDistPath, "locales", "*"), | ||
| }, | ||
| { find: "@emdash-cms/admin", replacement: useSource ? adminSourcePath : adminDistPath }, | ||
| ], |
There was a problem hiding this comment.
loadMessages() now relies on import.meta.glob("./**/messages.mjs") inside @emdash-cms/admin/locales. When core aliases @emdash-cms/admin to the admin source directory in dev (HMR mode), src/locales/*/messages.mjs typically doesn't exist (only messages.po), and the existing linguiMacroPlugin only rewrites explicit ./xx/messages.mjs imports (not import.meta.glob). This means the admin route can crash in monorepo dev unless lingui compile has been run.
Consider adding a more-specific Vite alias for @emdash-cms/admin/locales (and optionally /locales/) to always resolve to dist/locales (similar to the existing styles.css alias), or otherwise ensure the glob resolves against the compiled catalogs.
packages/admin/src/locales/index.ts
Outdated
| const LOCALE_LOADERS = import.meta.glob<{ messages: Record<string, unknown> }>("./**/messages.mjs"); | ||
|
|
||
| export async function loadMessages(locale: string): Promise<Record<string, unknown>> { | ||
| const key = `./${locale}/messages.mjs`; | ||
| const loader = LOCALE_LOADERS[key] ?? LOCALE_LOADERS["./en/messages.mjs"]!; | ||
| const { messages } = await loader(); | ||
| return messages; |
There was a problem hiding this comment.
loadMessages() hard-codes the fallback key to ./en/messages.mjs instead of using DEFAULT_LOCALE. If the default locale ever changes, this function will silently diverge from the single source of truth in config.ts.
Also, if catalogs haven’t been compiled yet, LOCALE_LOADERS["./en/messages.mjs"] will be undefined and the current code will fail with a generic "loader is not a function" error. Consider using DEFAULT_LOCALE for the fallback key and throwing a clear error when neither the requested locale nor the fallback catalog exists (e.g., instructing to run lingui compile).
| import { expect, test } from "vitest"; | ||
|
|
||
| import { loadMessages, SUPPORTED_LOCALES } from "../../src/locales/index.js"; | ||
|
|
||
| for (const { code } of SUPPORTED_LOCALES) { | ||
| test(`loadMessages resolves catalog for supported locale "${code}"`, async () => { | ||
| const messages = await loadMessages(code); | ||
| expect(messages).toBeDefined(); | ||
| expect(typeof messages).toBe("object"); | ||
| expect(Object.keys(messages).length).toBeGreaterThan(0); | ||
| }); |
There was a problem hiding this comment.
This test calls loadMessages() from src/locales/index.js, which depends on compiled messages.mjs catalogs existing on disk. Since the @emdash-cms/admin test script doesn’t run locale:compile (and messages.mjs files are gitignored), pnpm --filter @emdash-cms/admin test will fail on a clean checkout unless a build/compile step was run beforehand.
To keep tests self-contained, consider ensuring lingui compile runs before tests (e.g., via a pretest script) or having the test explicitly generate/fixture the catalogs it expects.
packages/admin/src/locales/index.ts
Outdated
| const LOCALE_LOADERS = import.meta.glob<{ messages: Record<string, unknown> }>("./**/messages.mjs"); | ||
|
|
||
| export async function loadMessages(locale: string): Promise<Record<string, unknown>> { | ||
| const key = `./${locale}/messages.mjs`; |
There was a problem hiding this comment.
The messages payload coming from Lingui catalogs is typed as Record<string, unknown> here, but downstream (e.g., AdminAppProps.messages and core’s AdminWrapperProps.messages) uses @lingui/core’s Messages type. Using Messages for import.meta.glob’s module type and loadMessages()’s return type would improve type safety and keep the locale API consistent across packages.
There was a problem hiding this comment.
@copilot apply changes based on this feedback
There was a problem hiding this comment.
Done in b49155a. The changes applied:
import.meta.globnow usesMessagesfrom@lingui/coreas the module typeloadMessages()return type changed fromPromise<Record<string, unknown>>toPromise<Messages>- Fallback key uses
DEFAULT_LOCALEinstead of the hardcoded"./en/messages.mjs"string - Added a clear error when neither the requested locale nor the fallback catalog is found (e.g., if
pnpm locale:compilehasn't been run)
…for fallback Agent-Logs-Url: https://github.com/emdash-cms/emdash/sessions/c16c31e9-70d6-4dcc-9a07-27184ed47cd8 Co-authored-by: ascorbic <213306+ascorbic@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
I have read the CLA Document and I hereby sign the CLA 1 out of 2 committers have signed the CLA. |
What does this PR do?
Fixes admin failing to load when
emdashis installed from npm (non-monorepo). The dynamicimport()inadmin.astroused a template literal to load locale catalogs cross-package (@emdash-cms/admin/locales/${locale}/messages.mjs), which Vite's SSR module runner couldn't resolve.The Vite alias entry intended to handle this used
*in thefindstring, but Vite treats that as a literal character — so the alias was a no-op.Moves locale loading into the admin package via a
loadMessages()function that usesimport.meta.globto discover catalogs at build time with reliable relative paths.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runpnpm locale:extracthas been run (if applicable)AI-generated code disclosure
Screenshots / test output
Locale unit tests pass: