ianstormtaylor / superstruct

A simple and composable way to validate data in JavaScript (and TypeScript).
https://docs.superstructjs.org
MIT License
6.96k stars 223 forks source link

Fix compatibility with Node16/NodeNext moduleResolution #1200

Closed AsyncBanana closed 2 months ago

AsyncBanana commented 8 months ago

When setting ModuleResolution to NodeNext in a project's tsconfig.json and using ESM within that project, superstruct's types fail to be discovered. I am fairly sure this is due to the fact that index.d.ts imports other type declarations without file extensions.

It would be a fairly simple fix to change imports to use .js file extensions, which do work, even for type declarations, and shouldn't break anything.

mnahkies commented 8 months ago

Duplicates #1160

Would be nice to get a resolution to this / #1160 - just ran into this today 🤞

ciscoheat commented 8 months ago

Same here, cannot integrate Superstruct in Superforms v2 due to this.

ciscoheat commented 7 months ago

Opened a PR for this issue in #1211

nerochiaro commented 5 months ago

I am testing version 1.0.5-0 which, as I understand it, is a pre-release for the fix to this issue. It works OK if I put "moduleResolution":"node16" in my tsconfig.json.

However it seems to fail for me when I also use "module": "node16" along with it. This is what is recommended for node 20 by https://www.npmjs.com/package/@tsconfig/node20 so I would expect superstruct to work with this common configuration. But maybe I am misunderstanding something.

My index.ts file is like this:

import { object, string } from 'superstruct';
const User = object({ foo: string() });

and I get this error:

index.ts:1:32 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("superstruct")' call instead.
  To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/home/ugo/superstructure-test/package.json'.

1 import { object, string } from 'superstruct';

Am I missing something or should this work too ?

arturmuller commented 2 months ago

@nerochiaro The TS error actually explains it correctly — you are importing an ECMAScript module (superstruct in this case) from a CommonJS module. That's currently not allowed by Node.js.

The underlying cause is a bit obscured by TypeScript in this case, but it's the equivalent of doing something like this:

// File: index.cjs <- a CommonJS module because of .cjs
const { hello } = require("./hello.mjs")
hello()
// File: hello.mjs <- a ECMAScript module because of .mjs
export function hello() {
  console.log("hello world")
}

Trying to run this with Node you get the following error:

Error [ERR_REQUIRE_ESM]: require() of ES Module <path>/hello.mjs not supported.
Instead change the require of <path>/hello.mjs to a dynamic import() which is available in all CommonJS modules.

In TypeScript codebases, I find it best to set the package.json type field to "module", which solves this issue.

{
  "type": "module",

  // ...plus all the other usual fields
}
arturmuller commented 2 months ago

This has been resolved in 2.0!