Skip to content

@object-ui/app-shell still cannot take the nodenext pin: @monaco-editor/react lazy import is TS2345 under Node16/NodeNext #5440

Description

@os-support-ai

Blocked-by: #5439

Found while implementing #5365. That card's acceptance was "the consumer-side pin goes green", and it does for @object-ui/fields (235 errors to 0, pin landed). @object-ui/app-shell went from 1097 to 23, so the pin was used as a measuring instrument there and deliberately NOT landed — the residue is not #5365's defect and would have made the pin a red enforcement on day one.

(Generics below are written with a space after < — GitHub's body sanitizer strips < followed by a letter as an HTML tag, and it ate them on the first revision of this issue.)

What was measured, 2026-08-20

Worktree off main32ef595f4, full closure built, pnpm --filter @object-ui/app-shell exec tsc --noEmit:

state of packages/app-shell/tsconfig.jsontypings of components/layouterrors
nodenext pinbefore #53651097 — 880 TS2305, 215 TS7006, 2 TS2345
nodenext pinafter #536523 — 21 TS7006, 2 TS2345
no pin (control)after #53650

The control is the load-bearing row: app-shell type-checks clean today, so all 23 are produced by the pin.

The 21 TS7006 are #5439, not a defect here

They are implicit-any parameters at call sites of symbols imported from @object-ui/plugin-chatbot, which ships extensionless typings of its own. Because that package re-exports through NAMED re-exports rather than export *, the names still exist and merely lose their types — see #5439 for the two-way probe. Fixing that package should clear all 21; nothing in app-shell needs to change for them.

The 2 TS2345 are a real, separate decision

src/views/metadata-admin/JsonSourceEditor.tsx(30,31): error TS2345:
Argument of type '() => Promise< { default: typeof import("@monaco-editor/react"); } >'
is not assignable to parameter of type '() => Promise< { default: ComponentType< any >; } >'.

Same shape at src/views/metadata-admin/previews/SourcePageEditor.tsx(30,31). Both are React.lazy(() => import('@monaco-editor/react')).

Under bundler resolution the module's default export reads as the component. Under node16/nodenext the CJS/ESM interop shape of @monaco-editor/react@4.7.0 resolves differently and the namespace object — not the component — lands in default, so React.lazy rejects it. This is a genuine interop question about a third-party dependency, not an extension question, and the answer is a choice:

  • take the named export (import('@monaco-editor/react').then(m => ({ default: m.Editor }))) — smallest, and arguably more correct than relying on a default;
  • add the dependency to an interop-shim surface;
  • leave app-shell unpinned until the dependency itself ships dual typings.

Worth a decision rather than a guess, because the same interop class will meet every other CJS-shaped dependency the moment another consumer pins.

Acceptance

packages/app-shell/tsconfig.json carries the two module/moduleResolution: nodenext lines that packages/react and packages/fields already carry, and pnpm --filter @object-ui/app-shell type-check is green.

Refs #5365, #5357, #4538.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions