vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.78k stars 26.95k forks source link

(Next.js + Jest) Import breaks when alias uses same name as relative import #55166

Open martenbjork opened 1 year ago

martenbjork commented 1 year ago

Link to the code that reproduces this issue or a replay of the bug

https://github.com/martenbjork/next-conflict-reproduce

To Reproduce

  1. Set up nextjs. Use default import alias (@/)
  2. Set up jest using instructions in Next.js docs (app router). next/js copies the import aliases from tsconfig.
  3. Set up the following structure:
utils.ts
/components
    something.ts
    something.test.ts
    utils.ts

Inside the local utils, we export a value:

export const LOREM = 123;

Inside the global utils.ts, we export a value

export const IPSUM = 456;

Now, inside something.ts, we attempt to import the values from the 2 separate utils.ts:

import { LOREM } from "./utils";
import { IPSUM } from "@/utils";

console.log({ LOREM, IPSUM }};
  1. Run yarn jest

Current vs. Expected behavior

Current: console.log log returns {LOREM: 123, IPSUM: undefined} Expected: console.log returns {LOREM: 123, IPSUM: 456}

My take

Hacky workaround

Fix

Verify canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.6.0: Wed Jul  5 22:21:53 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T6020
    Binaries:
      Node: 18.17.0
      npm: 9.6.7
      Yarn: 1.22.19
      pnpm: N/A
    Relevant Packages:
      next: 13.4.20-canary.23
      eslint-config-next: 13.4.19
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.2.2
    Next.js Config:
      output: N/A

Which area(s) are affected? (Select all that apply)

Jest (next/jest)

Additional context

No response

Moranilt commented 1 year ago

I have the same issue with my mappers:

'^@store/slices/(.*)$': '<rootDir>/components/redux-storage/slices/$1',
'^@store/hooks': '<rootDir>/components/redux-storage/hooks',
'^@store/local': '<rootDir>/components/redux-storage/localStorage',
'^@store': '<rootDir>/components/redux-storage/store',

Works fine with next@13.4.19 and lower. Starting from next@13.5.1 all tests fails with errors: Cannot find module '../../../components/redux-storage/@store' from '__tests__/pages/services/buycard.test.tsx' Cannot find module '../local' from 'components/redux-storage/slices/auth.ts' Cannot find module '../../../components/redux-storage/local' from 'utils/services/fillcard/dataReducer.ts'

Dependencies:

"@jest/types": "^29.5.0",
"@testing-library/jest-dom": "^5.16.5",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"next": "^13.5.2"

jest.config.js:

const nextJest = require('next/jest');

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
});

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/__tests__/jest.setup.ts'],
  testMatch: ['**/*.test.tsx'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@components/(.*)$': '<rootDir>/components/$1',
    '^@store/slices/(.*)$': '<rootDir>/components/redux-storage/slices/$1',
    '^@store/hooks': '<rootDir>/components/redux-storage/hooks',
    '^@store/local': '<rootDir>/components/redux-storage/localStorage',
    '^@store': '<rootDir>/components/redux-storage/store',
    '^@layouts/(.*)$': '<rootDir>/components/layouts/$1',
    '^@containers/(.*)$': '<rootDir>/containers/$1',
    '^@utils/(.*)$': '<rootDir>/utils/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async

module.exports = async () => {
  const newJestConfig = await createJestConfig(customJestConfig)();
  return ({
    ...(newJestConfig),
    transformIgnorePatterns: ['/node_modules/(?!(react-hook-form|@hookform/resolvers|dexie)/)'],
  });
};
boredland commented 11 months ago

I just ran into this "Cannot find module" issue again when upgrading to v14 coming from <13.4

rzubrycki commented 3 months ago

Any update on this? on newest next I got the same error.

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!

Moranilt commented 2 months ago

I have the same issue with my mappers:

'^@store/slices/(.*)$': '<rootDir>/components/redux-storage/slices/$1',
'^@store/hooks': '<rootDir>/components/redux-storage/hooks',
'^@store/local': '<rootDir>/components/redux-storage/localStorage',
'^@store': '<rootDir>/components/redux-storage/store',

Works fine with next@13.4.19 and lower. Starting from next@13.5.1 all tests fails with errors: Cannot find module '../../../components/redux-storage/@store' from '__tests__/pages/services/buycard.test.tsx' Cannot find module '../local' from 'components/redux-storage/slices/auth.ts' Cannot find module '../../../components/redux-storage/local' from 'utils/services/fillcard/dataReducer.ts'

Dependencies:

"@jest/types": "^29.5.0",
"@testing-library/jest-dom": "^5.16.5",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"next": "^13.5.2"

jest.config.js:

const nextJest = require('next/jest');

const createJestConfig = nextJest({
  // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
  dir: './',
});

// Add any custom config to be passed to Jest
const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/__tests__/jest.setup.ts'],
  testMatch: ['**/*.test.tsx'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@components/(.*)$': '<rootDir>/components/$1',
    '^@store/slices/(.*)$': '<rootDir>/components/redux-storage/slices/$1',
    '^@store/hooks': '<rootDir>/components/redux-storage/hooks',
    '^@store/local': '<rootDir>/components/redux-storage/localStorage',
    '^@store': '<rootDir>/components/redux-storage/store',
    '^@layouts/(.*)$': '<rootDir>/components/layouts/$1',
    '^@containers/(.*)$': '<rootDir>/containers/$1',
    '^@utils/(.*)$': '<rootDir>/utils/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
};

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async

module.exports = async () => {
  const newJestConfig = await createJestConfig(customJestConfig)();
  return ({
    ...(newJestConfig),
    transformIgnorePatterns: ['/node_modules/(?!(react-hook-form|@hookform/resolvers|dexie)/)'],
  });
};

My siolution was to rename alias @store/local into @local-store. I think it depends on nexted names with the same(almost) patterns like ^@store/hooks and ^@store/local.

Final config:

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/__tests__/jest.setup.ts'],
  testMatch: ['**/*.test.tsx'],
  moduleNameMapper: {
    // Handle module aliases (this will be automatically configured for you soon)
    '^@components/(.*)$': '<rootDir>/components/$1',
    '^@store/slices/(.*)$': '<rootDir>/components/redux-storage/slices/$1',
    '^@store/hooks$': '<rootDir>/components/redux-storage/hooks',
    '@local-store$': '<rootDir>/components/redux-storage/localStorage',
    '@store$': '<rootDir>/components/redux-storage/store',
    '^@layouts/(.*)$': '<rootDir>/components/layouts/$1',
    '^@containers/(.*)$': '<rootDir>/containers/$1',
    '^@utils/(.*)$': '<rootDir>/utils/$1',
  },
  testEnvironment: 'jest-environment-jsdom',
};