Skip to content

feat: support monaco-editor 0.56 - #2

Merged
murrayju merged 2 commits into
mainfrom
murrayju/monaco-0.56
Aug 4, 2026
Merged

feat: support monaco-editor 0.56#2
murrayju merged 2 commits into
mainfrom
murrayju/monaco-0.56

Conversation

@murrayju

@murrayju murrayju commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds support for monaco-editor 0.56 (which this package currently cannot build against), and modernizes the build chain onto Node 24 / TypeScript 5 while we're in here.

Two commits, separately reviewable:

08b3038 feat: support monaco-editor 0.56 — the actual fix
c162de2 chore: modernize the build chain onto node 24 and typescript 5

This is blocking timescale/popsql#4913, which is carrying a patch-package patch against this package as a stopgap. Once this is released, that patch can be deleted.

Part 1 — monaco 0.56 support

0.56 reorganized the ESM tree and added an exports map. The pgsql definition extends monaco's own monarch grammar rather than duplicating it, so it imports basic-languages/pgsql/pgsql directly — and under 0.56 that specifier resolves to nothing:

Module not found: Package path ./esm/vs/basic-languages/pgsql/pgsql is exported
from package monaco-editor, but no valid target file was found

Two things have to change: the definitions moved to languages/definitions/<lang>/<lang>, and the path must go through the exports map (monaco-editor/<path>, not monaco-editor/esm/vs/<path>) with its .js extension — the map's wildcards rewrite ./<path>./esm/vs/<path>.js and match literally, so the extensionless form points at a file that doesn't exist.

No single specifier spans versions, which is why the peer range has to narrow:

specifier 0.38 0.52 0.55 0.56
esm/vs/basic-languages/pgsql/pgsql
esm/vs/basic-languages/pgsql/pgsql.js
languages/definitions/pgsql/pgsql.js

So peerDependencies goes *>=0.56.0 rather than silently mis-resolving on older versions.

Three incidental fixes, each required to get the existing suites running on 0.56 — none cosmetic:

  • babel — 0.56 locates workers with new URL(..., import.meta.url), a syntax error once Jest transforms those modules to CJS, so the whole editor.api import chain failed to load. Added babel-plugin-transform-import-meta; tests only tokenize strings and never start a worker, so it's inert at runtime.
  • jest moduleNameMapper — stripped the package name but didn't apply 0.56's esm/vs/ rewrite, so the new specifier resolved nowhere. Added a rule mirroring the exports map, ahead of the existing generic rule (which transformIgnorePatterns depends on).
  • test mocks — 0.56 builds its icon stylesheet via CSS.escape, absent in jsdom, which threw inside setMonarchTokensProvider before any tokenization ran.

Part 2 — toolchain modernization

The old toolchain made the above awkward: pinning babel-plugin-transform-import-meta to 2.x purely to keep @babel/template off Node ≥22, and mapping monaco's declarations through tsconfig paths because TS 4.9 has no resolution mode that reads exports.

  • Node ≥24, in engines + .nvmrc, and the only version CI builds. The old matrix (14/16/18/20/22) couldn't survive this — monaco 0.56, jest 30, and current typescript-eslint all need newer runtimes.
  • TypeScript 4.9 → 5.9, jest 29 → 30, prettier 2 → 3, husky 8 → 9, lint-staged 13 → 17, rimraf 5 → 6, typescript-eslint 5 → 8, babel to current.
  • eslint stays on 8, deliberately. Moving to 9+ requires flat config, and both eslint-config-airbnb-base (unmaintained, eslint ≤8) and eslint-plugin-typescript-sort-keys (eslint ≤8) block it. Worth doing, but that's a rewrite of the lint setup rather than a version bump — happy to take it as a follow-up.
  • CI now also runs yarn lint, installs with --frozen-lockfile (a stale lockfile fails instead of being silently rewritten), and is off the deprecated actions/*@v2/@v3. The publish workflow ran Node 16, which engines would now reject outright.

With TS 5 available, the paths mapping from the first commit is replaced by resolution modes that genuinely understand exports: bundler for ESM, node16 for CJS (bundler requires an ES module; plain CommonJS/node10 can't see monaco's declarations at all). Verified the CJS output is still CommonJS — module: node16 emits either form depending on context.

Packaging bug found along the way

exports listed types last, after import/require. Node and TypeScript take the first matching condition, so the declarations were never selected and consumers silently got no types at all. Now nested under . with types first, plus a ./package.json entry.

Confirmed against a scratch consumer with a pristine 0.56 install: import { pgsqlLanguageDefinition } from '@popsql/monaco-sql-languages' now typechecks, where before it failed with "Cannot find module ... or its corresponding type declarations". This bug predates this branch — it's a real improvement for consumers independent of the monaco work.

The example app pinned monaco ^0.38.0 (now violating the peer range) with a react-monaco-editor too old to accept 0.56; both bumped, since the publish workflow builds that site.

Verification

Verified against a pristine 0.56 install — deliberately not the patched copy popsql carries — by building, installing as a consumer would, and awaiting the lazy loader over both CJS and ESM:

  • pgsqlLanguageDefinition.loader() → 657 builtin functions, pg17 additions present (json_table)
  • timescaleLanguageDefinition.loader() → 825 (correctly layering on pgsql)
  • consumer-side tsc --moduleResolution bundler → clean

From a clean clone on Node 24: install, lint, 228 tests, and both build targets pass. CI green on 24.x.

Release note

No version bump in the diff — the publish workflow sets the version from the release name. This needs a release cut (0.4.0 seems right, given the peer-dependency narrowing and the Node floor) before popsql can drop its patch.

0.56 reorganized the ESM tree and added an `exports` map, and the combination
breaks the one deep import this package makes. The pgsql definition extends
monaco's own monarch grammar rather than duplicating it, so it imports
`basic-languages/pgsql/pgsql` directly. Under 0.56 that specifier resolves to
nothing, and any consumer bundling this package fails with:

  Module not found: Package path ./esm/vs/basic-languages/pgsql/pgsql is exported
  from package monaco-editor, but no valid target file was found

Two separate things have to change to fix it:

  - The definitions moved to `languages/definitions/<lang>/<lang>`.
  - The path must go through the exports map (`monaco-editor/<path>`, not
    `monaco-editor/esm/vs/<path>`), and must carry its `.js` extension. The map's
    wildcards rewrite `./<path>` to `./esm/vs/<path>.js` and match the result
    literally, so the extensionless form resolves to a file that does not exist.

Verified end to end against a *pristine* 0.56 install -- deliberately not the
patched copy popsql carries -- by building the package, installing it as a
consumer would, and awaiting the lazy loader over both CJS and ESM. pgsql resolves
with 657 builtin functions including the pg17 additions, and timescale (which
layers on pgsql) resolves with 825.

Because 0.56 is now the floor, `peerDependencies` narrows from `*` to `>=0.56.0`
rather than silently mis-resolving on older versions. The devDependency moves from
^0.38.0 to ^0.56.0 so CI tests what consumers actually get.

Three incidental fixes, each needed to keep the existing suites running on 0.56:

  - tsconfig: 0.56's `typings` field points at ./esm/vs/editor/editor.main.d.ts,
    which the package no longer ships; the real declarations are at
    ./esm/vs/index.d.ts, reachable only through `exports`. Since
    `moduleResolution: node` ignores `exports`, TypeScript was falling back to the
    untyped ./min bundle and failing every `import ... from 'monaco-editor'` with
    an implicit any. Mapped explicitly via `paths`. Upgrading TypeScript to 5.x
    and using a resolution mode that honors `exports` would be the cleaner fix,
    but that is a larger change than this one warrants.
  - babel: 0.56 locates its workers with `new URL(..., import.meta.url)`, which is
    a syntax error once Jest transforms those modules to CJS, so the whole
    editor.api import chain failed to load. Added
    babel-plugin-transform-import-meta. Tests only tokenize strings and never
    start a worker, so the rewrite is inert at runtime.
  - jest: `moduleNameMapper` stripped the package name but did not apply 0.56's
    `esm/vs/` rewrite, so the new specifier resolved nowhere. Added a rule that
    mirrors the exports map, ahead of the existing generic rule that still handles
    already-`esm/...` paths.
  - test mocks: 0.56 builds its icon stylesheet through `CSS.escape`, absent in
    jsdom, which threw inside setMonarchTokensProvider before any tokenization ran.

All 228 tokenization tests pass, lint is clean, and both build targets emit.
@murrayju
murrayju force-pushed the murrayju/monaco-0.56 branch from c2c1c94 to 08b3038 Compare August 4, 2026 16:01
This repo had not been touched in a couple of years, and the previous commit was
sitting on a toolchain old enough to make the monaco 0.56 support awkward: pinning
babel-plugin-transform-import-meta to 2.x purely to keep @babel/template off node
>=22, and mapping monaco's declarations through tsconfig `paths` because
TypeScript 4.9 has no resolution mode that reads `exports`.

Toolchain
- Node >=24, declared in `engines` and .nvmrc, and the only version CI builds.
  The old matrix (14/16/18/20/22) could not survive this: monaco 0.56, jest 30 and
  the current typescript-eslint all require newer runtimes.
- TypeScript 4.9 -> 5.9, jest 29 -> 30, prettier 2 -> 3, husky 8 -> 9,
  lint-staged 13 -> 17, rimraf 5 -> 6, typescript-eslint 5 -> 8, babel to current.
- eslint stays on 8. Moving to 9+ means flat config, and both
  eslint-config-airbnb-base (unmaintained, eslint <=8) and
  eslint-plugin-typescript-sort-keys (eslint <=8) block that. Worth doing, but it
  is a rewrite of the lint setup rather than a version bump, so it is left alone
  here.
- CI also runs `yarn lint` now, and installs with --frozen-lockfile so a stale
  lockfile fails the build instead of being silently rewritten. Actions bumped off
  the deprecated v2/v3 releases.
- The publish workflow ran node 16, which `engines` would now reject outright.

TypeScript resolution
With TypeScript 5 available, the `paths` mapping added in the previous commit is
replaced by resolution modes that actually understand `exports`: `bundler` for the
ESM build, `node16` for the CJS build (`bundler` requires an ES module, and plain
`CommonJS`/`node10` cannot see monaco's declarations at all). Verified the CJS
output is still CommonJS -- `module: node16` emits either form depending on
context.

Packaging fix
`exports` listed `types` last, after `import` and `require`. Node and TypeScript
take the first matching condition, so the type declarations were never selected
and consumers silently got none. Nested under `.` with `types` first, plus a
`./package.json` entry. Confirmed against a scratch consumer with a pristine 0.56
install: `import { pgsqlLanguageDefinition } from '@popsql/monaco-sql-languages'`
now typechecks, where before it failed with "Cannot find module ... or its
corresponding type declarations". This bug predates this branch.

The example app pinned monaco ^0.38.0, which now violates the peer range, and its
react-monaco-editor was too old to accept 0.56. Both bumped; the publish workflow
builds this site.

Verified from a clean clone on node 24: install, lint, 228 tests, and both build
targets all pass, and the built package still resolves and typechecks from a
consumer against an unpatched monaco 0.56 (pgsql 657 builtins, timescale 825, over
both CJS and ESM).
@murrayju
murrayju merged commit 1d50b6a into main Aug 4, 2026
1 check passed
@murrayju
murrayju deleted the murrayju/monaco-0.56 branch August 4, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant