Skip to content

fix(parser): ESM entry points fail with ERR_UNSUPPORTED_DIR_IMPORT - #181

Merged
pyramation merged 1 commit into
mainfrom
devin/1792896839-parser-esm-types
Oct 25, 2026
Merged

pyramation merged 1 commit into
mainfrom
devin/1792896839-parser-esm-types

Conversation

@pyramation

Copy link
Copy Markdown
Collaborator

Summary

In Node, import ... from '@pgsql/parser' (and every @pgsql/parser/vNN subpath) throws when loaded with ESM import. require works. Every published version I checked has this bug: 1.2.1, 1.5.0, 1.6.0 and 1.6.1.

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '.../@pgsql/parser/wasm/v13/types' is not supported
resolving ES modules imported from .../@pgsql/parser/wasm/v13/index.js

Cause: parser/scripts/prepare.js rewrites @pgsql/types to the bundled ./types directory in every copied file. Node's ESM resolver rejects directory imports. CJS require('./types') and .d.ts resolution accept them, so only the ESM .js files break. Bundlers resolve directories, which is probably why nobody reported it.

Fix: ESM .js files now get ./types/index.js. .cjs and .d.ts keep ./types.

Test: the new parser/test/esm.test.mjs imports the built root entry and each @pgsql/parser/vNN subpath with ESM import, then parses. Until now the parser tests only used require('../wasm/index.cjs'), so this path never ran in CI.

Repro evidence (local, parser/wasm built from the published 13.8.1–18.2.1 WASMs):

  • Without the fix: esm.test.mjs fails with ERR_UNSUPPORTED_DIR_IMPORT for v13–v18.
  • With the fix, pnpm test in parser/ passes 33/33 on Node 24. esm.test.mjs passes 7/7 on Node 20.20 and 22.23 too.
  • Applying the same rewrite to the published @pgsql/parser@1.6.1 tarball makes ESM import work on Node 20, 22 and 24.

Link to Devin session: https://app.devin.ai/sessions/bcd8b2f791164247acb7a659b481efc2
Open in Devin Desktop: https://app.devin.ai/desktop/session/bcd8b2f791164247acb7a659b481efc2?variant=devin
Requested by: @pyramation

@devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tenki-reviewer

tenki-reviewer Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Review complete. 🟡 2 medium

📍 Findings outside the diff (2) — 🟡 2 medium — defects on lines GitHub can't attach comments to

🟡 Medium — exports map advertises subpaths an LTS build does not ship · package.json:15–44 · unchanged line

// parser/package.json
15	    "./v13": {
16	      "import": "./wasm/v13.js",
17	      "require": "./wasm/v13.cjs",
18	      "types": "./wasm/v13.d.ts"
19	    },
20	    "./v14": {
21	      "import": "./wasm/v14.js",
22	      "require": "./wasm/v14.cjs",
23	      "types": "./wasm/v14.d.ts"
24	    },
25	    "./v15": {
26	      "import": "./wasm/v15.js",

parser/package.json hardcodes ./v13 through ./v18 in its exports map (parser/package.json:15), but a PARSER_BUILD_TYPE=lts run of prepare.js cleans wasm/ and materializes only v15-v18 (parser/scripts/prepare.js:9). import('@pgsql/parser/v13') then fails with ERR_MODULE_NOT_FOUND, and the new ESM test iterates build-info.json versions so it cannot catch the mismatch in an LTS build. Per PUBLISH.md, LTS builds are an officially published configuration, so consumers importing the advertised v13/v14 subpaths get a runtime error.


🟡 Medium — build-info.json records versions whose wasm copy failed · prepare.js:210–213 · unchanged line

// parser/scripts/prepare.js
210	fs.writeFileSync(
211	  path.join(wasmDir, 'build-info.json'),
212	  JSON.stringify(buildInfo, null, 2)
213	);

When a version's wasm source is missing, prepare.js only logs a warning and returns from that loop iteration (parser/scripts/prepare.js:106), yet buildInfo.versions is still written from the full config.versions list (parser/scripts/prepare.js:210). The generated v${version}.js re-export then points at a nonexistent ./v${version}/index.js, and parser/test/esm.test.mjs — which iterates exactly build-info.json's versions — fails with a module-not-found error at test/runtime instead of the build failing early. Metadata and bundled artifacts disagree, surfacing the misconfiguration late and confusingly.


The change reworks parser/scripts/prepare.js so that per-version generated .js, .cjs, and .d.ts files have their ./types type imports rewritten, and the build metadata written at the end reflects the configured version set. parser/test/esm.test.mjs now iterates the versions recorded in wasm/build-info.json and dynamically imports each @pgsql/parser/vN entry to confirm the re-exports resolve. parser/package.json is touched alongside, keeping the static ./v13-./v18 exports map in place for these subpaths.

Files Change
parser/scripts/prepare.js Rewrites type import specifiers in generated per-version files and records the built version list in build-info.json.
parser/test/esm.test.mjs New ESM test that loads every bundled version subpath listed in build-info.json.
parser/package.json Companion metadata touched as part of the packaging update.

Reviewed commit: d1d56e0

@pyramation
pyramation merged commit 5690f55 into main Oct 25, 2026
59 checks passed
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