Skip to content

fix(core): admin UI fails to load on Windows in dev mode - #2390

Open
dashimu wants to merge 1 commit into
emdash-cms:mainfrom
dashimu:fix/windows-admin-lingui-macro
Open

fix(core): admin UI fails to load on Windows in dev mode#2390
dashimu wants to merge 1 commit into
emdash-cms:mainfrom
dashimu:fix/windows-admin-lingui-macro

Conversation

@dashimu

@dashimu dashimu commented Aug 9, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes the admin UI failing to load in dev mode on Windows — it gets stuck on "Loading EmDash..." forever, with console errors like Could not resolve "babel-plugin-macros" and later process is not defined.

Root cause: linguiMacroPlugin (in packages/core/src/astro/integration/vite-config.ts) is a dev-only Vite plugin that compiles away @lingui/core/macro calls in admin source files before they reach the browser. It matches files via id.startsWith(adminSourcePath), where adminSourcePath comes straight out of path.resolve(). On Windows, resolve() returns backslash-separated paths, but Vite always normalizes the id/importer it passes to plugin hooks to forward slashes — even on Windows. So the startsWith check is silently always false there, the plugin's hooks never fire, and raw @lingui/core/macro imports ship to the browser. Vite's dependency optimizer then tries to pre-bundle that module as a real dependency, which fails at runtime (it's only meant to be consumed by a macro loader).

Three fixes, all in the same plugin:

  1. Normalize adminSourcePath to forward slashes before comparing against Vite's ids/importers (replaceAll("\\", "/"), not path.sep-driven, so it's also correct — and testable — when a Windows-style path is passed on a non-Windows host).
  2. Convert the dynamic import() of @babel/core to a file:// URL via pathToFileURL() — Node's ESM loader on Windows rejects a raw drive-letter path for dynamic import().
  3. Resolve @lingui/babel-plugin-lingui-macro to an absolute path via admin's own node_modules (same adminRequire used for @babel/core), instead of passing the bare specifier to Babel, which resolves plugin names starting from the transformed file's directory — outside admin's own dependency tree when EmDash is aliased into a demo/template app.

Verified manually end to end on Windows 11: seeded and ran both demos/simple and templates/marketing, confirmed the admin dashboard, welcome modal, and content list all render and hydrate correctly after the fix (previously stuck on the loading spinner in both).

Closes #

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: N/A — bug fix

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Sonnet 5 (Claude Code)

Screenshots / test output

New regression tests in packages/core/tests/unit/astro/vite-config.test.ts (linguiMacroPlugin Windows path-separator handling) reproduce the mismatch with a synthetic Windows-style path, independent of the host OS running the test — they fail against the pre-fix linguiMacroPlugin (in fact fail to even import it, since it wasn't exported) and pass after the fix.

✓ linguiMacroPlugin Windows path-separator handling > compiles away @lingui macro calls in admin source files
✓ linguiMacroPlugin Windows path-separator handling > redirects locale catalog imports from admin source to dist/locales
✓ linguiMacroPlugin Windows path-separator handling > does not match files outside admin source

Note: this test file has 5 pre-existing, unrelated failures on Windows (createViteConfig admin aliasing / use-sync-external-store shim aliasingTypeError: File URL path must be absolute, from a synthetic file:///workspace/emdash-site/ URL that isn't valid on Windows without a drive letter). Left untouched per the "no drive-by fixes" policy; happy to open a separate issue/PR for it if useful.

The Lingui macro-compiler plugin in dev mode compared Vite's
forward-slash module ids against `adminSourcePath` straight out of
`path.resolve()`, which uses backslash separators on Windows. The
`startsWith` check was therefore silently always false there, so the
plugin's hooks never matched anything, and admin `@lingui/core/macro`
calls shipped uncompiled to the browser. Vite then tried to pre-bundle
`@lingui/core/macro` as a real dependency, which fails at runtime
(babel-plugin-macros missing, then `process is not defined`) since
that module is only meant to be consumed by a macro loader.

Fixes by:
- Normalizing adminSourcePath to forward slashes before comparing
  against Vite's ids/importers.
- Converting the dynamic `import()` of @babel/core to a file:// URL,
  which Node's ESM loader requires for absolute paths on Windows.
- Resolving the Lingui babel plugin to an absolute path via admin's
  own node_modules, instead of letting Babel resolve the bare
  specifier relative to the transformed file (which may live outside
  admin's dependency tree, e.g. in a demo/template app).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b6a8ac

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR correctly diagnoses and fixes a Windows-specific dev-mode failure in the admin Lingui macro compiler. The approach is right: Vite normalizes module ids to forward slashes but path.resolve() returns backslashes on Windows, so the plugin’s startsWith check was a silent no-op. The patch applies three targeted fixes inside linguiMacroPlugin and adds cross-platform regression tests.

I read the changed plugin, test file, changeset, package dependencies, and public exports. The fixes are localized, the dependencies (@babel/core and @lingui/babel-plugin-lingui-macro live in @emdash-cms/admin devDependencies) are correct, and exporting linguiMacroPlugin only from the internal integration file does not change the public API.

The only issue is an AGENTS.md comment convention violation: the multi-line comment above the new describe block is a PR-description summary/historical narrative rather than code-readable context. It should be removed (the describe title and the test bodies already explain the behavior).

Comment on lines +184 to +196
// Regression: on Windows, `path.resolve()` returns backslash-separated
// paths, but Vite always normalizes module ids/importers to forward
// slashes — even on Windows. `linguiMacroPlugin` used to compare an
// `adminSourcePath` straight out of `resolve()` against those ids with
// `id.startsWith(adminSourcePath)`, which is silently always `false` on
// Windows. The plugin's hooks became permanent no-ops there: Lingui macro
// calls (`@lingui/core/macro`) shipped uncompiled to the browser, which
// then failed to hydrate the admin UI entirely (it never got past the
// "Loading EmDash..." screen). These tests reproduce that mismatch
// directly, with a synthetic backslash `adminSourcePath` — independent of
// the host OS running the test — so they fail on the pre-fix
// `id.startsWith(adminSourcePath)` comparison on any platform, not just
// Windows CI.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This large comment block is a PR-description summary: it narrates the bug being fixed, the pre-failure behavior, and how the tests reproduce it. AGENTS.md says comments are not PR descriptions or summaries of the change — that context belongs in the commit message/PR body, not in code. The describe title and the test names already explain the behavior; this block should be deleted.

Suggested change
// Regression: on Windows, `path.resolve()` returns backslash-separated
// paths, but Vite always normalizes module ids/importers to forward
// slashes — even on Windows. `linguiMacroPlugin` used to compare an
// `adminSourcePath` straight out of `resolve()` against those ids with
// `id.startsWith(adminSourcePath)`, which is silently always `false` on
// Windows. The plugin's hooks became permanent no-ops there: Lingui macro
// calls (`@lingui/core/macro`) shipped uncompiled to the browser, which
// then failed to hydrate the admin UI entirely (it never got past the
// "Loading EmDash..." screen). These tests reproduce that mismatch
// directly, with a synthetic backslash `adminSourcePath` — independent of
// the host OS running the test — so they fail on the pre-fix
// `id.startsWith(adminSourcePath)` comparison on any platform, not just
// Windows CI.
describe("linguiMacroPlugin Windows path-separator handling", () => {

@github-actions github-actions Bot added area/core size/M review/awaiting-author Reviewed; waiting on the author to respond labels Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@dashimu

dashimu commented Aug 9, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@dashimu

dashimu commented Aug 9, 2026

Copy link
Copy Markdown
Author

recheck

github-actions Bot added a commit that referenced this pull request Aug 9, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2390

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2390

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2390

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2390

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2390

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2390

emdash

npm i https://pkg.pr.new/emdash@2390

create-emdash

npm i https://pkg.pr.new/create-emdash@2390

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2390

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2390

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2390

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2390

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2390

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2390

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2390

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2390

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2390

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2390

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2390

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2390

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2390

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2390

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2390

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2390

commit: 6b6a8ac

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core cla: signed review/awaiting-author Reviewed; waiting on the author to respond size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant