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

build: allow use by TypeScript projects with bundler/node16 config #2084

Merged
merged 4 commits into from
Jan 2, 2024

Conversation

tash-2s
Copy link
Contributor

@tash-2s tash-2s commented Jan 2, 2024

This pull request enables MUD users with moduleResolution set to bundler or node16 in their tsconfig.json to pass tsc by generating .d.ts files during the build. After this change, users with bundler/node16 config can use MUD packages without being affected by MUD's internal TypeScript setup, as they won't need to use the .ts files of MUD packages.

Problem

Currently, moduleResolution: node10 is the only supported option for using MUD packages, which is not ideal for most users. For example, here is the config I'd like to use:

diff --git a/templates/vanilla/packages/client/tsconfig.json b/templates/vanilla/packages/client/tsconfig.json
index d8bb624b..d740aed8 100644
--- a/templates/vanilla/packages/client/tsconfig.json
+++ b/templates/vanilla/packages/client/tsconfig.json
@@ -1,18 +1,17 @@
 {
   "compilerOptions": {
     "types": ["vite/client"],
-    "target": "ESNext",
+    "target": "esnext",
     "useDefineForClassFields": true,
-    "module": "ESNext",
+    "module": "esnext",
     "lib": ["ESNext", "DOM"],
-    "moduleResolution": "Node",
+    "moduleResolution": "bundler",
     "strict": true,
     "resolveJsonModule": true,
     "isolatedModules": true,
     "esModuleInterop": true,
     "noEmit": true,
-    "skipLibCheck": true,
-    "jsx": "react-jsx"
+    "skipLibCheck": true
   },
   "include": ["src"]
 }
diff --git a/templates/vanilla/packages/contracts/tsconfig.json b/templates/vanilla/packages/contracts/tsconfig.json
index 270db8fd..da0a1f48 100644
--- a/templates/vanilla/packages/contracts/tsconfig.json
+++ b/templates/vanilla/packages/contracts/tsconfig.json
@@ -1,13 +1,13 @@
 // Visit https://aka.ms/tsconfig.json for all config options
 {
   "compilerOptions": {
-    "target": "ES2020",
-    "module": "commonjs",
+    "target": "esnext",
+    "module": "esnext",
     "strict": true,
     "resolveJsonModule": true,
     "esModuleInterop": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
-    "moduleResolution": "node"
+    "moduleResolution": "bundler"
   }
 }

However, this configuration results in the following error (similar errors also occur in client/):

$ cd templates/vanilla/packages/contracts
$ pnpm tsc --noEmit

mud.config.ts:1:27 - error TS7016: Could not find a declaration file for module '@latticexyz/world/register'. '/Users/t/ghq/github.com/tash-2s/mud/packages/world/dist/ts/register/index.js' implicitly has an 'any' type.
  There are types at '/Users/t/ghq/github.com/tash-2s/mud/templates/vanilla/packages/contracts/node_modules/@latticexyz/world/ts/register/index.ts', but this result could not be resolved when respecting package.json "exports". The '@latticexyz/world' library may need to update its package.json or typings.

1 import { mudConfig } from "@latticexyz/world/register";
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Found 1 error in mud.config.ts:1

This error occurs because, under the moduleResolution: bundler setting, tsc refers to the package.json exports field of the MUD package to find the .js file but fails to find the corresponding .d.ts file.

This issue can also be confirmed using the @arethetypeswrong/cli tool:

$ cd packages/common && attw --entrypoints . --pack .
...
┌───────────────────┬──────────────────────┐
│                   │ "@latticexyz/common" │
├───────────────────┼──────────────────────┤
│ node10            │ 🟢                   │
├───────────────────┼──────────────────────┤
│ node16 (from CJS) │ ❌ No types          │
├───────────────────┼──────────────────────┤
│ node16 (from ESM) │ ❌ No types          │
├───────────────────┼──────────────────────┤
│ bundler           │ ❌ No types          │
└───────────────────┴──────────────────────┘

Solution

To resolve this, this PR enables the generation of .d.ts files during the build. MUD packages use tsup for TypeScript builds, and simply setting dts: true in tsup.config.ts suffices.

.ts files cannot replace .d.ts files in libraries. Using .ts files as type declarations forces users to align their tsconfig.json with that of the libraries.

The targeted packages are under ./packages/, except for:

  • Empty packages: ecs-browser, network, solecs, std-client, std-contracts, store-cache
  • Packages with no exports: create-mud
  • Packages with exports but empty files: cli, store-indexer
  • Packages not imported from TypeScript: solhint-config-mud, solhint-plugin-mud

With this change, the previously mentioned errors are resolved, and the project works without issues. In projects configured with bundler or node16, tsc no longer asks for the installation of libraries like @types/prettier and @types/react-dom. Additionally, tsc --noEmit time decreased from 13 seconds to 4 seconds under templates/vanilla/packages/client on my mac, as tsc does not need to process MUD's .ts files.

@arethetypeswrong/cli outputs results like this for the packages:

┌───────────────────┬──────────────────────────────┐
│                   │ "@latticexyz/common"         │
├───────────────────┼──────────────────────────────┤
│ node10            │ 🟢                           │
├───────────────────┼──────────────────────────────┤
│ node16 (from CJS) │ ⚠️ ESM (dynamic import only) │
├───────────────────┼──────────────────────────────┤
│ node16 (from ESM) │ 🟢 (ESM)                     │
├───────────────────┼──────────────────────────────┤
│ bundler           │ 🟢                           │
└───────────────────┴──────────────────────────────┘

(MUD packages are pure ESM and are not expected to be used from CJS.)

This change does not impact existing users with node10, as their tsc does not refer to the exports fields in MUD packages, and the built .js files within MUD packages, used by their bundlers, remain unchanged.

The downside is an increase in build time. However, it is not significant, as tsup generates .js and .d.ts files in parallel. Here is a comparison on my mac between the main branch and this branch using a build command that disables all cache: git clean -d -f -x -q && pnpm install && time pnpm build --force.

Main branch (dts disabled):

 Tasks:    24 successful, 24 total
Cached:    0 cached, 24 total
  Time:    1m7.039s 

pnpm build --force  82.02s user 6.78s system 131% cpu 1:07.78 total

This branch (dts enabled):

 Tasks:    24 successful, 24 total
Cached:    0 cached, 24 total
  Time:    1m38.516s 

pnpm build --force  212.04s user 13.61s system 227% cpu 1:39.27 total

Before this change:

```
$ cd packages/dev-tools
$ pnpm tsc --declaration

src/router.tsx:12:14 - error TS2742: The inferred type of 'router' cannot be named without a reference to '.pnpm/@remix-run+router@1.6.0/node_modules/@remix-run/router'. This is likely not portable. A type annotation is necessary.

12 export const router = createMemoryRouter(
                ~~~~~~

Found 1 error in src/router.tsx:12
```
Without this change, error on tsup:

```
Error: namespace must have a "ModuleBlock" body.

> 1 | declare module "prettier-plugin-solidity";
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
Copy link

changeset-bot bot commented Jan 2, 2024

🦋 Changeset detected

Latest commit: 257e2f9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
@latticexyz/abi-ts Patch
@latticexyz/block-logs-stream Patch
@latticexyz/common Patch
@latticexyz/config Patch
@latticexyz/dev-tools Patch
@latticexyz/faucet Patch
@latticexyz/gas-report Patch
@latticexyz/noise Patch
@latticexyz/phaserx Patch
@latticexyz/protocol-parser Patch
@latticexyz/react Patch
@latticexyz/recs Patch
@latticexyz/schema-type Patch
@latticexyz/services Patch
@latticexyz/store-sync Patch
@latticexyz/store Patch
@latticexyz/utils Patch
@latticexyz/world-modules Patch
@latticexyz/world Patch
@latticexyz/cli Patch
@latticexyz/store-indexer Patch
create-mud Patch
@latticexyz/ecs-browser Patch
@latticexyz/network Patch
@latticexyz/solecs Patch
solhint-config-mud Patch
solhint-plugin-mud Patch
@latticexyz/std-client Patch
@latticexyz/std-contracts Patch
@latticexyz/store-cache Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@@ -1 +1 @@
declare module "prettier-plugin-solidity";
declare module "prettier-plugin-solidity" {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tsup's internal dts generation process doesn't support the original syntax, so I've changed it to a format that both tsc and tsup accept. There is no change in meaning in this context. Without this, the following error occurs on tsup:

Error: namespace must have a "ModuleBlock" body.

> 1 | declare module "prettier-plugin-solidity";
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@holic
Copy link
Member

holic commented Jan 2, 2024

With this change, the previously mentioned errors are resolved, and the project works without issues. In projects configured with bundler or node16, tsc no longer asks for the installation of libraries like @types/prettier and @types/react-dom. Additionally, tsc --noEmit time decreased from 13 seconds to 4 seconds under templates/vanilla/packages/client on my mac, as tsc does not need to process MUD's .ts files.

💯 🎉

@holic
Copy link
Member

holic commented Jan 2, 2024

Thank you! CI looks happy, so I'm gonna merge this.

Is there anything CI-wise we should wire up to ensure we can keep this level of compatibility?

@holic holic merged commit 5905420 into latticexyz:main Jan 2, 2024
10 of 11 checks passed
@tash-2s
Copy link
Contributor Author

tash-2s commented Jan 2, 2024

Is there anything CI-wise we should wire up to ensure we can keep this level of compatibility?

Good point. We should ensure CI covers all supported user environments to avoid accidentally breaking compatibility, so I should add a test for non-node10 tsconfig settings. I'll write this task in the original issue.

@tash-2s tash-2s deleted the typescript-setup-improvement-step-1 branch January 2, 2024 19:22
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.

2 participants