Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TSC throws an error when compiling with "moduleResolution": "NodeNext" #1381

Closed
Sec-ant opened this issue Oct 23, 2022 · 19 comments · Fixed by #1387
Closed

TSC throws an error when compiling with "moduleResolution": "NodeNext" #1381

Sec-ant opened this issue Oct 23, 2022 · 19 comments · Fixed by #1387
Labels
help wanted Please someone help on this

Comments

@Sec-ant
Copy link

Sec-ant commented Oct 23, 2022

When I want to import the default create function from zustand/vanilla while setting NodeNext for both module and moduleResolution in tsconfig.json, TSC will throw an error: error TS2349: This expression is not callable.

I mentioned this issue in microsoft/TypeScript#50058 (comment). However, according to the reply, this is a zustand problem:

@zustand/vanilla has incorrect types, again, it only declares types for a cjs entrypoint for everything, and that is the result of that mismatch.

Kindly request a fix on this problem, thanks!

In the meantime, I use default-import as a temporary patch.

this doesn't work

import create from "zustand/vanilla";
export const store = create(() => ({ foo: "bar" }));

this works

import zustand from "zustand/vanilla";
import { defaultImport } from "default-import";
const create = defaultImport(zustand);
export const store = create(() => ({ foo: "bar" }));

A minimal repo to reproduce this problem: https://stackblitz.com/edit/node-vj368k?file=src/index.ts

@dai-shi
Copy link
Member

dai-shi commented Oct 24, 2022

Thanks for reporting.

Can anyone elaborate this issue? How can we declare types for mjs entrypoints?

(It looks the same if you import create from zustand. https://stackblitz.com/edit/node-mcrpwh?file=src%2Findex.ts )

@dai-shi dai-shi added the help wanted Please someone help on this label Oct 24, 2022
@Sec-ant Sec-ant changed the title TSC throw errors when compiling with "moduleResolution": "NodeNext" TSC throws an error when compiling with "moduleResolution": "NodeNext" Oct 24, 2022
@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

I think I find the reason why zustand does not work properly as an esm import.

While I was searching for related problems, I bumped into this solved issue: hayes/pothos#597, which was also an esm module resolution problem. The author finally got his package fixed after several pushes and I noticed one of his comments:

thanks, I think I see what's going in. Looks like some imports in the esm definintions are not working correctly because they are importing directories rather than explicitly asking for dir/index.js

and

... Needed to transform all the imports to include the full file path...

So I first checked his package.json:

  "main": "./lib/index.js",
  "types": "./dts/index.d.ts",
  "module": "./esm/index.js",
  "exports": {
    "import": {
      "default": "./esm/index.js"
    },
    "require": {
      "types": "./dts/index.d.ts",
      "default": "./lib/index.js"
    }
  },

And according to this configuration, typescript will use ./dts/index.d.ts when this module is resolved as a cjs module and will use ./esm/index.d.ts when it is resolved as an es module. By comparing these two typedef files, we can find that the .js extension is needed in all the export or import statements in an esm typedef file:

diff ./esm/index.d.ts ./dts/index.d.ts
@@ -1,11 +1,11 @@
-import './types/global';
-import SchemaBuilderClass from './builder';
-import type { FieldKind, NormalizeSchemeBuilderOptions, SchemaTypes } from './types';
-export * from './plugins';
-export * from './types';
-export * from './utils';
+import './types/global/index.js';
+import SchemaBuilderClass from './builder.js';
+import type { FieldKind, NormalizeSchemeBuilderOptions, SchemaTypes } from './types/index.js';
+export * from './plugins/index.js';
+export * from './types/index.js';
+export * from './utils/index.js';
 declare const SchemaBuilder: {
-    new <Types extends Partial<PothosSchemaTypes.UserSchemaTypes> = {}>(options: import("./types").RemoveNeverKeys<PothosSchemaTypes.SchemaBuilderOptions<PothosSchemaTypes.ExtendDefaultTypes<Types>>>): PothosSchemaTypes.SchemaBuilder<PothosSchemaTypes.ExtendDefaultTypes<Types>>;
+    new <Types extends Partial<PothosSchemaTypes.UserSchemaTypes> = {}>(options: import("./types/index.js").RemoveNeverKeys<PothosSchemaTypes.SchemaBuilderOptions<PothosSchemaTypes.ExtendDefaultTypes<Types>>>): PothosSchemaTypes.SchemaBuilder<PothosSchemaTypes.ExtendDefaultTypes<Types>>;
     registerPlugin: typeof SchemaBuilderClass.registerPlugin;
     allowPluginReRegistration: boolean;
 };
@@ -23,10 +23,10 @@ export declare type ObjectFieldBuilder<Types extends SchemaTypes, ParentShape> =
 export declare const ObjectFieldBuilder: new <Types extends SchemaTypes, ParentShape>(name: string, builder: SchemaBuilderClass<Types>) => PothosSchemaTypes.ObjectFieldBuilder<Types, ParentShape>;
 export declare type InterfaceFieldBuilder<Types extends SchemaTypes, ParentShape> = PothosSchemaTypes.InterfaceFieldBuilder<Types, ParentShape>;
 export declare const InterfaceFieldBuilder: new <Types extends SchemaTypes, ParentShape>(name: string, builder: SchemaBuilderClass<Types>) => PothosSchemaTypes.InterfaceFieldBuilder<Types, ParentShape>;
-export declare type InputFieldBuilder<Types extends SchemaTypes, Kind extends 'Arg' | 'InputObject' = 'Arg' | 'InputObject'> = PothosSchemaTypes.InputFieldBuilder<Types, Kind>;
+export declare type InputFieldBuilder<Types extends SchemaTypes, Kind extends "Arg" | "InputObject" = "Arg" | "InputObject"> = PothosSchemaTypes.InputFieldBuilder<Types, Kind>;
 export declare const InputFieldBuilder: new <Types extends SchemaTypes, Kind extends "InputObject" | "Arg" = "InputObject" | "Arg">(builder: SchemaBuilderClass<Types>, kind: Kind, typename: string) => PothosSchemaTypes.InputFieldBuilder<Types, Kind>;
 export declare type BaseTypeRef = PothosSchemaTypes.BaseTypeRef;
-export declare const BaseTypeRef: new (kind: 'Enum' | 'InputObject' | 'Interface' | 'Object' | 'Scalar' | 'Union', name: string) => PothosSchemaTypes.BaseTypeRef;
+export declare const BaseTypeRef: new (kind: "Enum" | "InputObject" | "Interface" | "Object" | "Scalar" | "Union", name: string) => PothosSchemaTypes.BaseTypeRef;
 export declare type EnumRef<T, P = T> = PothosSchemaTypes.EnumRef<T, P>;
 export declare const EnumRef: new <T, P = T>(name: string) => PothosSchemaTypes.EnumRef<T, P>;
 export declare type InputObjectRef<T> = PothosSchemaTypes.InputObjectRef<T>;
@@ -39,13 +39,13 @@ export declare type ScalarRef<T, U, P = T> = PothosSchemaTypes.ScalarRef<T, U, P
 export declare const ScalarRef: new <T, U, P = T>(name: string) => PothosSchemaTypes.ScalarRef<T, U, P>;
 export declare type UnionRef<T, P = T> = PothosSchemaTypes.UnionRef<T, P>;
 export declare const UnionRef: new <T, P = T>(name: string) => PothosSchemaTypes.UnionRef<T, P>;
-export { default as BuildCache } from './build-cache';
-export { default as BuiltinScalarRef } from './refs/builtin-scalar';
-export { default as FieldRef } from './refs/field';
-export { default as InputTypeRef } from './refs/input';
-export { default as InputFieldRef } from './refs/input-field';
-export { ImplementableInputObjectRef } from './refs/input-object';
-export { ImplementableInterfaceRef } from './refs/interface';
-export { ImplementableObjectRef } from './refs/object';
-export { default as OutputTypeRef } from './refs/output';
-//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
+export { default as BuildCache } from './build-cache.js';
+export { default as BuiltinScalarRef } from './refs/builtin-scalar.js';
+export { default as FieldRef } from './refs/field.js';
+export { default as InputTypeRef } from './refs/input.js';
+export { default as InputFieldRef } from './refs/input-field.js';
+export { ImplementableInputObjectRef } from './refs/input-object.js';
+export { ImplementableInterfaceRef } from './refs/interface.js';
+export { ImplementableObjectRef } from './refs/object.js';
+export { default as OutputTypeRef } from './refs/output.js';
+//# sourceMappingURL=index.d.ts.map

And the reason is that typescript doesn't mutate any import or export statements when it compiles user's source code to generate typedefs. Therefore, if a package using typescript is not of type module while planning to support esm build and allowing other esm typescript projects to import it, the package will have to also include typedefs specifically for esm imports.

And that's why this problem occurs in zustand because it only generated cjs typedefs, i.e. no ".js" extensions in the export or import statements:

index.d.ts, this affects zustand:

export * from './vanilla';
export * from './react';
export { default as createStore } from './vanilla';
export { default } from './react';

middleware/devtools.d.ts, this and other middleware typedefs affect zustand/vanilla:

import type { StateCreator, StoreApi, StoreMutatorIdentifier } from '../vanilla';

declare module '../vanilla' {
    interface StoreMutators<S, A> {
        'zustand/devtools': WithDevtools<S>;
    }
}

I am not familiar with the bundle/build scripts in generating or transforming these files, but I hope the above provided info can help you or other contributors locate the problem and fix it. ❤️

@dai-shi
Copy link
Member

dai-shi commented Oct 25, 2022

@Sec-ant Thanks! Good investigation.
One thing I'm not sure is our vanilla.d.ts file doesn't have any imports from other files. So, why do you still have the issue with import create from 'zustand/vanilla'?

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

One thing I'm not sure is our vanilla.d.ts file doesn't have any imports from other files. So, why do you still have the issue with import create from 'zustand/vanilla'?

I think it is caused by the extended declarations in the middleware type files:

declare module '../vanilla' {
    interface StoreMutators<S, A> {
        'zustand/devtools': WithDevtools<S>;
    }
}

But I'm not sure, because I didn't import any middleware in my example. Maybe typescript will analyze all the type files even if they are not imported?

@dai-shi
Copy link
Member

dai-shi commented Oct 25, 2022

@Sec-ant If you have a chance, can you try hand-patch the d.ts files in your local folder (or use patch-package) and see if adding .js resolves the issue??

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

@dai-shi OK, I made a simple test, and it seems that the .js extension is not the only problem, and hand patching them will not fix it (at least not for vanilla). It seems to be another totally different problem. I still do not fully understand whether it is a Typescript bug or not, but I'll show what I've got so far:

  1. I cloned the zustand repo as zustand-patch locally

  2. I ran the following commands to build:

    cd zustand-patch
    yarn
    yarn run build
  3. Under the same path where zustand-patch is located, I used yarn init in another empty dir called zustand-esm-test

  4. I used the following comands to install zustand-patch and typescript as dependencies

    yarn add zustand@link:../zustand-patch/dist
    yarn add -D typescript
  5. In zustand-esm-test, the package.json, tsconfig.json and src/index.ts are respectively:

    package.json
    {
      "name": "zustand-esm-test",
      "version": "1.0.0",
      "main": "index.js",
      "license": "MIT",
      "type": "module",
      "exports": {
        "types": "./dist/index.d.ts",
        "import": "./dist/index.js",
        "node": "./dist/index.js",
        "default": "./dist/index.js"
      },
      "scripts": {
        "build": "yarn cache clean && tsc",
        "trace": "yarn cache clean && tsc --traceResolution --noEmit | grep vanilla",
        "test": "node ./dist/index.js"
      },
      "dependencies": {
        "zustand": "link:../zustand-patch/dist"
      },
      "devDependencies": {
        "typescript": "^4.8.4"
      }
    }
    tsconfig.json
    {
      "compilerOptions": {
        "outDir": "dist",
        "target": "ESNext",
        "module": "NodeNext",
        "moduleResolution": "NodeNext",
        "declaration": true,
        "declarationDir": "dist"
      },
      "include": ["src/**/*"]
    }
    src/index.ts
    import create from "zustand/vanilla";
    export const store = create(() => ({ foo: "bar" }));
  6. If I run yarn run trace in zustand-esm-test without modifying any files in the zustand-patch, the error occurs, and the output is:

    output
    success Cleared cache.
    ======== Resolving module 'zustand/vanilla' from '/home/secant/zustand-esm-test/src/index.ts'. ========
    Loading module 'zustand/vanilla' from 'node_modules' folder, target file type 'TypeScript'.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts' exist - use it as a name resolution result.
    Resolving real path for '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts', result '/home/secant/zustand-patch/dist/vanilla.d.ts'.
    ======== Module name 'zustand/vanilla' was successfully resolved to '/home/secant/zustand-patch/dist/vanilla.d.ts' with Package ID 'zustand/vanilla.d.ts@4.1.3'. ========
      Type 'typeof import("/home/secant/zustand-patch/dist/vanilla")' has no call signatures.
    Done in 3.11s.
    
  7. If I delete ./vanilla.d.ts under zustand-patch/dist/, and in zustand-esm-test run yarn run trace, TSC can only find the .mjs file but no types (even under the esm directory, because TSC is looking for vanilla.d.mts not vanilla.d.ts), however this will build successfully (which is expected actually because no type is found):

    output
    success Cleared cache.
    ======== Resolving module 'zustand/vanilla' from '/home/secant/zustand-esm-test/src/index.ts'. ========
    Loading module 'zustand/vanilla' from 'node_modules' folder, target file type 'TypeScript'.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts' does not exist.
    File name '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.mjs' has a '.mjs' extension - stripping it.
    File '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.mts' does not exist.
    File '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.d.mts' does not exist.
    File name '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.js' has a '.js' extension - stripping it.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.ts' does not exist.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.tsx' does not exist.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts' does not exist.
    Export specifier './vanilla' does not exist in package.json scope at path '/home/secant/zustand-esm-test/node_modules/zustand'.
    Loading module 'zustand/vanilla' from 'node_modules' folder, target file type 'JavaScript'.
    File name '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.mjs' has a '.mjs' extension - stripping it.
    File '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.mjs' exist - use it as a name resolution result.
    Resolving real path for '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.mjs', result '/home/secant/zustand-patch/dist/esm/vanilla.mjs'.
    ======== Module name 'zustand/vanilla' was successfully resolved to '/home/secant/zustand-patch/dist/esm/vanilla.mjs' with Package ID 'zustand/esm/vanilla.mjs@4.1.3'. ========
    Done in 3.01s.
    
  8. From above we can see that TSC respects the exports field in the package.json. But it seems that something is wrong with the type file, so I revert all the modifications and change the content in ./vanilla.d.ts and run yarn run trace again. Well, again failed. I also tried remove all the middlewares but the error remained:

    modified ./vanilla.d.ts
    declare const createStore: () => void
    export default createStore
    output
    success Cleared cache.
    ======== Resolving module 'zustand/vanilla' from '/home/secant/zustand-esm-test/src/index.ts'. ========
    Loading module 'zustand/vanilla' from 'node_modules' folder, target file type 'TypeScript'.
    File '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts' exist - use it as a name resolution result.
    Resolving real path for '/home/secant/zustand-esm-test/node_modules/zustand/vanilla.d.ts', result '/home/secant/zustand-patch/dist/vanilla.d.ts'.
    ======== Module name 'zustand/vanilla' was successfully resolved to '/home/secant/zustand-patch/dist/vanilla.d.ts' with Package ID 'zustand/vanilla.d.ts@4.1.3'. ========
      Type 'typeof import("/home/secant/zustand-patch/dist/vanilla")' has no call signatures.
    Done in 2.92s.
    
  9. And then I tried copy the ./vanilla.d.ts and ./esm/vanilla.js files into zustand-esm-test/src, and use import create from "./vanilla.js"; to import and everything works just fine:

    src/index.ts
    import create from "./vanilla.js";
    export const store = create(() => ({ foo: "bar" }));
    output
    success Cleared cache.
    ======== Resolving module './vanilla.js' from '/home/secant/zustand-esm-test/src/index.ts'. ========
    Loading module as file / folder, candidate module location '/home/secant/zustand-esm-test/src/vanilla.js', target file type 'TypeScript'.
    File name '/home/secant/zustand-esm-test/src/vanilla.js' has a '.js' extension - stripping it.
    File '/home/secant/zustand-esm-test/src/vanilla.ts' does not exist.
    File '/home/secant/zustand-esm-test/src/vanilla.tsx' does not exist.
    File '/home/secant/zustand-esm-test/src/vanilla.d.ts' exist - use it as a name resolution result.
    ======== Module name './vanilla.js' was successfully resolved to '/home/secant/zustand-esm-test/src/vanilla.d.ts'. ========
    Done in 2.90s
    
  10. At this point, it seems that there is nothing wrong with types either. I'm starting to doubt that there's something buggy in typescript. 😮‍💨

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

I have some promising findings:

If I change the package.json > exports > ./vanilla > types field from "./vanilla.d.ts" to "./esm/vanilla.d.mts" like this:

"./vanilla": {
  "types": "./esm/vanilla.d.mts",
  "module": "./esm/vanilla.js",
  "import": "./esm/vanilla.mjs",
  "default": "./vanilla.js"
},

and add a new file: dist/esm/vanilla.d.mts whose content is copied from dist/esm/vanilla.d.ts (We can safely copy the content because no import or export is used in vanilla, so there's no .js extension issues, but of course in order for other exported subpaths to work, we'll have to consider this difference). We can finally get a successful output:

success Cleared cache.
======== Resolving module 'zustand/vanilla' from '/home/secant/zustand-esm-test/src/index.ts'. ========
Loading module 'zustand/vanilla' from 'node_modules' folder, target file type 'TypeScript'.
File '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.d.mts' exist - use it as a name resolution result.
Resolving real path for '/home/secant/zustand-esm-test/node_modules/zustand/esm/vanilla.d.mts', result '/home/secant/zustand-patch/dist/esm/vanilla.d.mts'.
======== Module name 'zustand/vanilla' was successfully resolved to '/home/secant/zustand-patch/dist/esm/vanilla.d.mts' with Package ID 'zustand/esm/vanilla.d.mts@4.1.3'. ========
Done in 1.82s

As I understand it, the .d.mts extension is telling TSC to regard this type file as a type file of a package imported as an esmodule rather than a commonjs module, or otherwise because we don't have type: "module" field in the package.json, TSC seems cannot decide which type of module we are expecting.

@dai-shi
Copy link
Member

dai-shi commented Oct 25, 2022

Just about package.json:
Does this work?

"./vanilla": {
  "types": "./vanilla.d.ts",
  "module": "./esm/vanilla.js",
  "import": {
    "types": "./esm/vanilla.d.mts", // trying to apply this only for "import"
    "default": "./esm/vanilla.mjs",
  },
  "default": "./vanilla.js"
},

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

Just about package.json: Does this work?

"./vanilla": {
  "types": "./vanilla.d.ts",
  "module": "./esm/vanilla.js",
  "import": {
    "types": "./esm/vanilla.d.mts", // trying to apply this only for "import"
    "default": "./esm/vanilla.mjs",
  },
  "default": "./vanilla.js"
},

Unfortunately no, TSC will use ./vanilla.d.ts as the resolution result, but if you change the order like this, it will work:

"./vanilla": {
  "import": {
    "types": "./esm/vanilla.d.mts",
    "default": "./esm/vanilla.mjs"
  },
  "types": "./vanilla.d.ts",
  "module": "./esm/vanilla.js",
  "default": "./vanilla.js"
},

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

I think there might be a better way to handle this, we can add a package.json file with type: "module" in the esm folder, just like what @pothos/core did, and do not use types field in the beginning, but let TSC find the types automatically.

a package.json in esm folder

{
  "type": "module"
}

and something like this in the toplevel package.json:

"./vanilla": {
  "module": "./esm/vanilla.js",
  "import": "./esm/vanilla.js",
  "default": "./vanilla.js"
}

will work.

We can also use the .mjs extension but then we'll have to generate a .d.mts type file along with it.

@dai-shi
Copy link
Member

dai-shi commented Oct 25, 2022

a package.json in esm folder

{
  "type": "module"
}

We did that once in some project, and there was a problem at that time. So, I would like to avoid it if the "changing the order" hack works.

@Sec-ant
Copy link
Author

Sec-ant commented Oct 25, 2022

We did that once in some project, and there was a problem at that time. So, I would like to avoid it if the "changing the order" hack works.

Ok, I'm fine with this approach. But if that's the case, I'm concerned that adding ".js" extensions may also break some of the packages.

@dai-shi
Copy link
Member

dai-shi commented Oct 25, 2022

I'm concerned that adding ".js" extensions may also break some of the packages.

Is it ☝️ about adding .js in *.d.mts files?


So, in summary, what we need to do is:

  1. modify package.json so that "import" condition picks esm/*.d.mts.
  2. rename esm/*d.ts to esm/*.d.mts.
  3. add .js extension for import/export statements in esm/*.d.mts.

Sounds okay?

@Sec-ant
Copy link
Author

Sec-ant commented Oct 26, 2022

Is it ☝️ about adding .js in *.d.mts files?

Yes, I'm not sure whether this will break other runtime or bundlers. Specifically, what concerns me is this.

So, in summary, what we need to do is:

  1. modify package.json so that "import" condition picks esm/*.d.mts.
  2. rename esm/*d.ts to esm/*.d.mts.
  3. add .js extension for import/export statements in esm/*.d.mts.

Sounds okay?

Basically yes. But 3 can be tricky if the transformation is applied on cjs type emits after build. And in our case, I think the extension should be .mjs rather than .js. I also tried to create and hand patch all the *.d.mts files (adding extensions), but the monorepo structure and somewhat a little complicated type declarations seemed to keep me hitting the walls. I'll leave these to you guys, but I'd be glad to do some testing if you need 😄

@dai-shi
Copy link
Member

dai-shi commented Oct 26, 2022

@Sec-ant Can you try #1387?

I'm a little skeptical about the mjs extension.

//  dist/esm/index.d.mts 
export * from './vanilla.mjs';
export * from './react.mjs'; // We don't have such a file!!!
export { default as createStore } from './vanilla.mjs';
export { default } from './react.mjs';

@Sec-ant
Copy link
Author

Sec-ant commented Oct 26, 2022

image
I think we should take those extended declarations into consideration too?

declare module '../vanilla.mjs' { // <= not '../vanilla'
    interface StoreMutators<S, A> {
        'zustand/redux': WithRedux<S, A>;
    }
}

After I hand patched them, there were no more resolution errors, and I can get rid of default-import now and build successfully :)

My current project is not using react, so I need to look further into that.

And one more thing, do we need to keep the .js files in the esm folder? Are they kept for other tools that do not recognize .mjs files? If so, will renaming .d.ts to .d.mts break those tools?

@dai-shi
Copy link
Member

dai-shi commented Oct 26, 2022

I think we should take those extended declarations into consideration too?

Good point.

After I hand patched them, there were no more resolution errors, and I can get rid of default-import now and build successfully :)

That's great to hear.

My current project is not using react, so I need to look further into that.

Can you quickly check if any of middleware (except for immer) works for you? It's the same as react.mjs.

And one more thing, do we need to keep the .js files in the esm folder? Are they kept for other tools that do not recognize .mjs files?

Yes, for "module" condition.

If so, will renaming .d.ts to .d.mts break those tools?

Ah, now I get it. Yeah, it's safer to keep them.

@Sec-ant
Copy link
Author

Sec-ant commented Oct 26, 2022

Can you quickly check if any of middleware (except for immer) works for you? It's the same as react.mjs.

Already did that, I use subscribeWithSelector in my project and it works fine after all the extensions being added.

I also made a simple test on react, and I didn't find any problems:

👇 This piece of code will build just fine (with a NodeNext module resolution config):

import React from "react";
import create from "zustand";
import { subscribeWithSelector } from "zustand/middleware";

interface BearState {
  bears: number;
}

interface BearControls {
  increasePopulation: () => void;
}

const useBearStore = create<BearState & BearControls>()(
  subscribeWithSelector((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  }))
);

export function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return <h1>{bears} around here ...</h1>;
}

export function Controls() {
  const increasePopulation = useBearStore((state) => state.increasePopulation);
  return <button onClick={increasePopulation}>one up</button>;
}

@dai-shi
Copy link
Member

dai-shi commented Oct 26, 2022

Sounds great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Please someone help on this
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants