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

Dynamic imports of ".ts" files occurring in node_modules package fail with "TypeError: Unknown file extension ".ts"" in an ESM project #7060

Open
6 tasks done
TheRealSmaft opened this issue Dec 10, 2024 · 4 comments

Comments

@TheRealSmaft
Copy link

Describe the bug

Dynamically importing files with the ".ts" extension from a package dependency throws ERR_UNKNOWN_FILE_EXTENSION when starting a service through a Vitest setup file. Note that both the service and the package dependency are ESM.

To demonstrate, I have a dependency that exports the following importFile utility:

import { parse } from 'path'

export const importFile = async (filePath: string) => {
  const fileName = parse(filePath).name
  const exports = await import(filePath)
  return {
    [fileName]: exports,
  }
}

And I have a service that calls the importFile function:

import { importFile } from 'fake-dep';
import { dirname, parse, resolve } from 'path';
import { fileURLToPath } from 'url';

const fileName = fileURLToPath(import.meta.url);
const dirName = dirname(fileName);
const dir = resolve(dirName, './dir/');

importFile(`${dir}/some-file.ts`); // Throws ERR_UNKNOWN_FILE_EXTENSION with .ts extension

importFile(`${dir}/some-file.js`); // Throws ERR_MODULE_NOT_FOUND with .js extension

Running the service (with ts-node) works as expected and neither of these errors are thrown. But they are when running the Vitest suite.

But when exactly the same function exists directly in the service rather than in a dependency, it works as expected running with ts-node or through Vitest:

import { importFile as depImportFile } from 'fake-dep';
import { dirname, parse, resolve } from 'path';
import { fileURLToPath } from 'url';

const fileName = fileURLToPath(import.meta.url);
const dirName = dirname(fileName);
const dir = resolve(dirName, './dir/');

const localImportFile = async (filePath: string) => {
  const fileName = parse(filePath).name;
  const exports = await import(filePath);
  return {
    [fileName]: exports,
  };
};

// This works
localImportFile(`${dir}/some-file.ts`);

// This works too
localImportFile(`${dir}/some-file.js`);

// This doesn't work
depImportFile(`${dir}/some-file.ts`);

// Neither does this
depImportFile(`${dir}/some-file.js`);

Reproduction

https://stackblitz.com/edit/vitest-dynamic-import-in-node-modules-error?file=readme.md

System Info

System:
  OS: macOS 14.7.1
  CPU: (12) arm64 Apple M2 Pro
  Memory: 4.87 GB / 32.00 GB
  Shell: 5.9 - /bin/zsh
Binaries:
  Node: 18.20.4 - ~/.nvm/versions/node/v18.20.4/bin/node
  Yarn: 1.22.22 - /opt/homebrew/bin/yarn
  npm: 10.7.0 - ~/.nvm/versions/node/v18.20.4/bin/npm
npmPackages:
  vitest: ^2.1.8 => 2.1.8

Used Package Manager

yarn

Validations

@hi-ogawa
Copy link
Contributor

Interesting repro. Two errors are probably due to difference causes.

The first one ERR_UNKNOWN_FILE_EXTENSION with .ts extension is about externalized (i.e. not transformed by Vite) fake-dep importing .ts file, which causes NodeJs to simply reject such import("xxx.ts"). This needs to be fixed by test.server.deps.inline: ["fake-dep"] since Vitest by default doesn't transform `node_modules.

Then 2nd one is probably about js -> ts resolution and it's being solved upstream by vitejs/vite#18889. I tested with the pre-release and your repro works now https://stackblitz.com/edit/vitest-dynamic-import-in-node-modules-error-balcn66j?file=fake-service%2Fvitest.config.ts

@TheRealSmaft
Copy link
Author

Excellent news! Thank you!

@timofei-iatsenko
Copy link

I have similar problem, i try to test a webpack loader in integration with a webpack in the Vitest test. I have following configuration for webpack:

  return webpack({
 //...
    resolveLoader: {
      alias: {
        "@lingui/loader": path.resolve(__dirname, "../src/webpackLoader.ts"),
      },
    },
)

Somewhere inside of webpack loading of the "/src/webpackLoader.ts" happens, and similar error is thrown.

As far as i understand that is caused by similar reason, the call chain is vitest -> test -> webpack (node_modules) -> webpackLoader.ts which is not processed by Vitest.

I tried to add

test.server.deps.inline: ["webpack"] as well as test.server.deps.inline: ["loader-runner"] and test.server.deps.inline = true but nothing works, the loader from sourcecode is not processed by Vite.

Any ideas how to fix that? May be there is a way to manually instrument this import:

"@lingui/loader": path.resolve(__dirname, "../src/webpackLoader.ts"), so it will go thru the vitest server?

@timofei-iatsenko
Copy link

I probably got the reason. Most likely something inside a Webpack is importing a resolver using a require. Require is not hijacked by the Vite and Vitest, that's why nothing from what I've tried worked.

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

No branches or pull requests

3 participants