vite: Importing a .js typescript resource from a typescript file fails

Describe the bug

Importing a .js TypeScript resource from a TypeScript file fails with: Internal server error: Failed to resolve import

TypeScript wants users to write the same module specifiers as are used in the produced output if they want to use ES Modules. An extensive discussion about the topic can be found here: https://github.com/microsoft/TypeScript/issues/16577. As a result esbuild supports the same feature. Rollup also supports this if you add extensions: ['.js', '.ts'] to the resolve plugin config. Thus I would expect the same code to work in Vite.

I personally would like to be able to run my .ts files through either vite or tsc as needed in a "type": "module" repository.

Reproduction

hello.ts

export const sayHello = () => console.log('Hello');

index.ts

import { sayHello } from "./hello.js";

System Info

Output of npx envinfo --system --npmPackages vite --binaries --browsers:

System:
    OS: Linux 5.11 Arch Linux
    CPU: (4) x64 Intel(R) Core(TM) i5-4690K CPU @ 3.50GHz
    Memory: 477.69 MB / 15.57 GB
    Container: Yes
    Shell: 5.8 - /usr/bin/zsh
  Binaries:
    Node: 14.16.0 - /usr/bin/node
    Yarn: 1.22.10 - /usr/bin/yarn
    npm: 7.10.0 - /usr/bin/npm
  Browsers:
    Brave Browser: 90.1.23.71
    Firefox: 87.0

Used package manager: yarn

Logs

[vite] Internal server error: Failed to resolve import "./hello.js" from "./index.ts". Does the file exist?
  Plugin: vite:import-analysis

Before submitting the issue, please make sure you do the following

  • Read the Contributing Guidelines.
  • Read the docs.
  • Check that there isn’t already an issue that reports the same bug to avoid creating a duplicate.
  • Provide a description in this issue that describes the bug.
  • Make sure this is a Vite issue and not a framework-specific issue. For example, if it’s a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/vue-next instead.
  • Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 21 (7 by maintainers)

Commits related to this issue

Most upvoted comments

FYI, in the latest team meeting, we’ve decided to add support for this feature:

  • For import requests initiated from a TS source file, if the module specifier ends with .js/jsx/.mjs/.cjs but the file doesn’t actually exist, Vite will try to import from its corresponding TypeScript file too.

My 2 cents, as someone who works with both Vite, TSC, and Node.js, on the same frontend code. We test extensively, and part of the test suite is running the frontend code using JSDOM in Node.js. For that reason, we use tsc to run our Node.js tests for the frontend, and of course vite to run it on the browser.

Given that we are using Node.js ESM, and type: module in our packages, we MUST specify an extension. The --experimental-specifier-resolution=node is not something we want to rely on for all our code (as a former member of the Node.js ESM working group, this option was added in the early days of the working group as a compromise between those that wanted an “extensionless” ESM and those that wanted to force extension use. It may be deprecated in the future, and even if not, the Node.js ecosystem has decided and is going with extensions).

Given that other bundlers are adding functionality to enable this (admittedly) weird behavior in TS (whereby you specify a .js file even if you want to import a .ts file), I believe it is in Vite’s interest to add this in some way, and would gladly help in implementing it if needed.

@Avocher As a workaround, you can run tsc --watch on the shared codebase and vite dev for the rest of the frontend code.


@giltayar Thanks for the information. I’ll bring this issue to the next team meeting and discuss it with other teammates.

I don’t know the exact reason that you need your .ts files to be able to run by either vite or tsc.

My bad, I should probably have started by saying: we have a quite large library of code that we share between frontend and backend, that’s the reason why we need both vite and tsc.

I added the rollup resolve plugin to the config as a temporary fix:

import { defineConfig } from 'vite'
import resolve from '@rollup/plugin-node-resolve'

export default defineConfig({
    plugins: [resolve({
        extensions: ['.js', '.ts']
    })]
})

It works for both development and building

seems like it can be resolved via the vite.config file as follows:

import { defineConfig } from 'vite'
import reactRefresh from '@vitejs/plugin-react-refresh'

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      {
        find:/^(.*)\.js$/,
        replacement: '$1',
      }
    ]
  },
  plugins: [reactRefresh()],
})