microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.25k stars 12.39k forks source link

AutoImport for internal package in monorepo stops working if more than a few dependencies are installed #58709

Closed michaelschufi closed 3 months ago

michaelschufi commented 3 months ago

πŸ”Ž Search Terms

"intellisense not working", "auto import", "autoimport", "internal package", "monorepo internal"

πŸ•— Version & Regression Information

Versions checked

❯ every-ts switch main
HEAD is now at 5df3a107c0 Update dependencies (#58639)

❯ every-ts switch 5.5
HEAD is now at b574864abc Update LKG

❯ every-ts switch 5.1
HEAD is now at 6e4aa901f2 Bump version to 5.1.6 and LKG

❯ every-ts switch 4.8
HEAD is now at a614119c19 Bump version to 4.8.4 and LKG

❯ every-ts switch 5.5
HEAD is now at b574864abc Update LKG

❯ every-ts switch 5.3
HEAD is now at 3a36bec244 πŸ€– Pick PR #56626 (Directly copy only the index signat...) into release-5.3 (#56709)

❯ every-ts switch 5.2
HEAD is now at 9684ba6b0d Cherry-pick fix for cross-file inlay hints (#55476) to release-5.2 and LKG (#55487)

I've used VS Code Insiders so I don't have any excess extensions (just WSL).

Version: 1.90.0-insider (system setup)
Commit: c6e45e96a6b0fe94e0dae5b13ab4167d69ec9788
Date: 2024-05-28T10:48:57.080Z
Electron: 29.4.0
ElectronBuildId: 9593362
Chromium: 122.0.6261.156
Node.js: 20.9.0
V8: 12.2.281.27-electron.0
OS: Windows_NT x64 10.0.19045

⏯ Playground Link

No response

πŸ’» Code

Reproduction Repo

See: https://github.com/michaelschufi/repro-monorepo-tsserver-import

Internal Package package.json

{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "main": "./dist/index.js",
  "exports": {
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx",
    "./code": "./src/code.tsx",
    "./get-foo": "./src/get-foo.ts",
    "./get-foobar": "./src/get-foo-bar.ts"
  },
  "scripts": {
    "lint": "eslint . --max-warnings 0",
    "generate:component": "turbo gen react-component"
  },
  "dependencies": {
    "typescript": "*"
  },
  "devDependencies": {
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@turbo/gen": "^1.12.4",
    "@types/node": "^20.11.24",
    "@types/eslint": "^8.56.5",
    "@types/react": "^18.2.61",
    "@types/react-dom": "^18.2.19",
    "eslint": "^8.57.0",
    "react": "^18.2.0",
    "typescript": "^5.3.3"
  }
}

get-foo.ts

export const getFoo = () => "foo"
export const getBar = () => "bar"

pnpx tsc --showConfig

{
    "compilerOptions": {
        "declaration": true,
        "declarationMap": true,
        "esModuleInterop": true,
        "incremental": false,
        "isolatedModules": true,
        "lib": [
            "es2022",
            "dom",
            "dom.iterable"
        ],
        "module": "nodenext",
        "moduleDetection": "force",
        "moduleResolution": "nodenext",
        "noUncheckedIndexedAccess": true,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "strict": true,
        "target": "es2022",
        "jsx": "react-jsx",
        "outDir": "./dist"
    },
    "files": [
        "./src/button.tsx",
        "./src/card.tsx",
        "./src/code.tsx",
        "./src/get-foo-bar.ts",
        "./src/get-foo.ts"
    ],
    "include": [
        "src",
        "src/get-foo-bar.ts"
    ],
    "exclude": [
        "node_modules",
        "dist"
    ]
}

App using internal package package.json

{
  "name": "web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --max-warnings 0"
  },
  "dependencies": {
    "@clerk/localizations": "^2.4.3",
    "@clerk/nextjs": "^5.1.2",
    "@tanstack/react-query": "^5.39.0",
    "@trpc/client": "11.0.0-rc.377",
    "@trpc/react-query": "11.0.0-rc.377",
    "@trpc/server": "11.0.0-rc.377",
    "@upstash/redis": "^1.31.3",
    "@repo/ui": "workspace:*",
    "lodash": "^4.17.21",
    "next": "^14.1.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "superjson": "^2.2.1",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@next/eslint-plugin-next": "^14.1.1",
    "@repo/eslint-config": "workspace:*",
    "@repo/typescript-config": "workspace:*",
    "@types/eslint": "^8.56.5",
    "@types/lodash": "^4.17.4",
    "@types/node": "^20.11.24",
    "@types/react": "^18.2.61",
    "@types/react-dom": "^18.2.19",
    "eslint": "^8.57.0",
    "typescript": "^5.3.3"
  }
}

page.tsx

const Page = () => {
  // import suggestion for lodash is working
  const bar = sum(1, 2)

  // internal @repo/ui package exports don't work
  const foo = getFoo();
  const bar = getBar();

  return (
    // also not in jsx
    <Card>
      <h1>Hello, world!</h1>
      <p>Welcome to your new app!</p>
    </div>
  );
};

export default Page;

pnpx tsc --showConfig

{
    "compilerOptions": {
        "declaration": true,
        "declarationMap": true,
        "esModuleInterop": true,
        "incremental": false,
        "isolatedModules": true,
        "lib": [
            "es2022",
            "dom",
            "dom.iterable"
        ],
        "module": "esnext",
        "moduleDetection": "force",
        "moduleResolution": "bundler",
        "noUncheckedIndexedAccess": true,
        "resolveJsonModule": true,
        "skipLibCheck": true,
        "strict": true,
        "target": "es2022",
        "plugins": [
            {
                "name": "next"
            }
        ],
        "allowJs": true,
        "jsx": "preserve",
        "noEmit": true
    },
    "files": [
        "./next-env.d.ts",
        "./next.config.js",
        "./app/route.ts",
        "./app/layout.tsx",
        "./app/page.tsx"
    ],
    "include": [
        "next-env.d.ts",
        "next.config.js",
        "**/*.ts",
        "**/*.tsx",
        ".next/types/**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

πŸ™ Actual behavior

Import suggestions for an internal package stop working if the number of installed dependencies grows too large. This already happens with as little as ~12 dependencies.

Reproduction

The reprodution repo is basically just Turborepo's basic example with a few dependencies added to showcase the autoimport failing.

pnpm dlx create-turbo@latest

from (Turborepo Getting Started). It uses the strategy for internal packages outlined here Turborepo Internal Packages - which is already preconfigured in the example.

Initial Steps

  1. Clone the repo from https://github.com/michaelschufi/repro-monorepo-tsserver-import.
  2. Go to the apps/web folder.
  3. Install the dependencies using pnpm pnpm i
  4. Open apps/web/app/page.tsx and try to import-suggest the getFoo function.
  5. Observe it not working.

Observing the strange behavior

  1. Remove a few dependencies (e.g. the first 3, I didn't try removing next, react or react-dom)
  2. Rerun pnpm i to remove them from node_modules.
  3. Restart the TS Server in VS Code.
  4. Observe the import suggestion showing the internal package import.

You can remove any other dependencies (as shown in the video). It doesn't matter which ones. I tried the following

"@clerk/localizations": "^2.4.3", // <-- Group 1
"@clerk/nextjs": "^5.1.2", // <-- Group 1
"@tanstack/react-query": "^5.39.0", // <-- Group 1
"@trpc/client": "11.0.0-rc.377",
"@trpc/react-query": "11.0.0-rc.377",
"@trpc/server": "11.0.0-rc.377", // <-- Group 3, not shown in the video
"@upstash/redis": "^1.31.3", // <-- Group 3, not shown in the video
"@repo/ui": "workspace:*",
"lodash": "^4.17.21",
"next": "^14.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"superjson": "^2.2.1", // <-- Group 2
"zod": "^3.23.8" // <-- Group 2

Removing any ?random? group of packages, makes the import work.

https://github.com/microsoft/TypeScript/assets/2805598/4bbab7d3-281b-4239-b4b2-9358bdcfde4b

Note The imports start showing up, if at least one file in the project has imported the file already. E.g. page.tsx imports

import { getFoo } from "@repo/ui/get-foo";

then, all imports from "@repo/ui/get-foo" are available when triggering the suggestion. Even if we are in e.g. layout.tsx.

But the imports from other paths of the internal package e.g. "@repo/ui/get-foobar" are not.

πŸ™‚ Expected behavior

I expect the imports to be working regardless of how many dependencies I have installed.

Additional information about the issue

Possibly related issues:

Below are the tsserver logs. Note the following lines in the first one

Info 282  [10:21:34.464] AutoImportProviderProject: attempted to add more than 10 dependencies. Aborting.

https://gist.github.com/michaelschufi/638a0a9bc5bc8568c86b50a8c0c0e723

RyanCavanaugh commented 3 months ago

Set "Include Package JSON Auto Imports" to "On" instead of "auto"

image

michaelschufi commented 3 months ago

Thank you so much! This seems to fix it πŸ˜€ Now, since it's not the default, I have to ask: What are the implications regarding performance if I enable that?

Maybe there could be some kind of whitelist, so we can include the internal packages in the VS Code settings?

typescript-bot commented 3 months ago

This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

NamGungGeon commented 1 month ago

Set "Include Package JSON Auto Imports" to "On" instead of "auto"

image

You save my life... thanks... πŸ˜‚

baptisteArno commented 1 week ago

OMG, that's a game changer!

This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

I'd be curious to know that too