Next.js + Jest moduleNameMapper without TypeScript - javascript

I am struggling to get moduleNameMapper to work with NextJS / JavaScript. For this project we are not using TypeScript.
Next: 12.2.5
React: 18.2.0
Jest: 29.0.1
Jest environment jsdom: 29.0.1
Here is my jsconfig.json file
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/components/*" : ["components/*"],
"#/styles/*" : ["styles/*"],
"#/config/*" : ["config/*"],
"#/hooks/*" : ["hooks/*"],
"#/store/*" : ["store/*"],
}
}
}
I can do the following in my project
import Layout from "#/components/Layout"
import useFetch from "#/hooks/fetch"
However, when I try to test I get this message
Cannot find module #/hooks/fetch from pages/account/login/index.js
Here is my jest.config.js file
const nextJest = require('next/jest');
const createJestConfig = nextJest({
dir: "./",
});
const customJestConfig = {
moduleDirectories: ["node_modules", "<rootDir>"],
moduleNameMapper: {
"^#/hooks/*": "/hooks/*"
},
testEnvironment: "jest-environment-jsdom",
}
module.exports = createJestConfig(customJestConfig)
As you can see I am trying to map the paths in moduleNameMapper but this still isn't working. How do I correct this?
I am hoping to use the same import formatting for both project and test project
many thanks

Managed to resolve this by changing the jsconfig.js file
const nextJest = require('next/jest');
const customJestConfig = {
moduleDirectories: ["node_modules", "<rootDir>"],
testEnvironment: "jest-environment-jsdom",
};
const createJestConfig = nextJest({
dir: './',
})(customJestConfig);
module.exports = async () =>
{
// Create Next.js jest configuration presets
const jestConfig = await createJestConfig();
// Custom `moduleNameMapper` configuration
const moduleNameMapper = {
...jestConfig.moduleNameMapper,
'^#/(.*)$': '<rootDir>/$1',
};
return { ...jestConfig, moduleNameMapper };
};
Source: https://github.com/vercel/next.js/issues/36682

Related

How to allow ts-jest to compile with errors

First time writing tests in typescript for my expo project. I've got some errors in my Typescript (the actual typing), but the functionality works. When I run my tests, ts-jest doesnt allow the tests to run since the types are incorrect (even though the functionality works). Is there some setting to allow ts-jest to run regardless of type errors?
App.test.tsx
import React from "react";
import renderer from "react-test-renderer";
import SignUp from "../screens/Auth/screens/SignUp";
describe("<App />", () => {
it("has 1 child", () => {
const tree = renderer.create(<SignUp />).toJSON();
console.log(tree);
});
});
jest.config.js
const { defaults: tsjPreset } = require("ts-jest/presets");
/** #type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: "react-native",
transform: {
"^.+\\.jsx$": "babel-jest",
"^.+\\.tsx?$": [
"ts-jest",
{
tsconfig: "tsconfig.spec.json",
},
],
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};
babel.config.js
/** #type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
}

Chart v4 : Module not found: Error: Package path . is not exported from package node_modules/chart.js

I'm maintaining a react component library that uses chart.js as a dependency (Not peerDependency).
I upgraded chart.js from 3.9.1 to 4.0.1.
My library still compiles fine but now it throws an error on my react app when I'm importing the lib :
Module not found: Error: Package path . is not exported from package node_modules/chart.js
I'm building everything with rollup here is my config :
import path from 'path';
import babel from '#rollup/plugin-babel';
import commonjs from '#rollup/plugin-commonjs';
import resolve from '#rollup/plugin-node-resolve';
import terser from '#rollup/plugin-terser';
import postcss from 'rollup-plugin-postcss';
import typescript from '#rollup/plugin-typescript';
import image from '#rollup/plugin-image';
import json from '#rollup/plugin-json';
import copy from 'rollup-plugin-copy';
import autoprefixer from 'autoprefixer';
import postcssUrl from 'postcss-url';
import pkg from './package.json' assert { type: 'json' };
import {fileURLToPath} from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { dependencies = {}, peerDependencies = {} } = pkg;
const externals = [...Object.keys(dependencies), ...Object.keys(peerDependencies)];
const src = path.resolve(__dirname, 'src');
const input = path.resolve(src, 'index.ts');
const assets = path.resolve(src, 'assets');
const dest = path.resolve(__dirname, 'dist');
export default [
{
input,
external: (id) => externals.some((dep) => id === dep || id.startsWith(`${dep}/`)),
plugins: [
typescript({ tsconfig: './tsconfig.json' }),
commonjs(),
json(),
resolve({ browser: true }),
babel({
extensions: ['.ts', '.js', '.tsx', '.jsx'],
}),
image(),
postcss({
plugins: [
autoprefixer,
postcssUrl({
url: 'inline',
basePath: assets,
}),
],
}),
copy({
targets: [
{ src: 'src/**/_*.scss.d.ts', dest },
{ src: 'src/**/_*.scss', dest },
],
}),
],
output: [
{
file: pkg.main,
format: 'cjs',
},
{ name: pkg.name, file: pkg.module, format: 'es' },
{
name: pkg.name,
file: pkg.browser,
format: 'umd',
globals: {
react: 'React',
},
},
{
name: pkg.name,
file: pkg['browser:min'],
format: 'umd',
globals: {
react: 'React',
},
plugins: [terser()],
},
],
},
];
Does someone has any idea why it is doing so ?
I tried to delete node_modules and package-lock.json and reinstall both on my library and front app but I still have the same error.
I tried to import from 'chart.js/auto' as mentioned in the documentation but it throws :
Module not found: Error: Package path ./auto is not exported from package node_modules/chart.js
I looked at node_modules/chart.js/package.json file and there is a well defined set of exports there. But as it came with the upgrade and no other library has ever throw me this error I guess it comes from the upgrade.
Found the bug. As I thought it is a problem with the library itself. It cannot be required since the conditional subpath is missing from the package.json file. Nothing I can do. I'll downgrade untill the push a fix.

Path mapping in Javascript?

I'm currently working on a javascript project. I usually use typescript and ts has the path mapping feature. Is there something similar in javascript ?
TS path mapping:
"compilerOptions": {
"baseUrl": "src",
...
"paths": {
"#app/*": ["app/*"],
"#config/*": ["app/_config/*"],
"#environment/*": ["environments/*"],
"#shared/*": ["app/_shared/*"],
"#helpers/*": ["helpers/*"]
},
...
In Webpack this feature is available via resolve.alias. The configuration is similar, for example via webpack.config.js:
const path = require('path');
module.exports = {
resolve: {
alias: {
"#app": path.resolve(__dirname, 'app/'),
"#config": path.resolve(__dirname, 'app/config/'),
"#environment": path.resolve(__dirname, 'environments/'),
// Etc
},
},
};

ts-jest cannot resolve tsconfig aliases

I have a typescript project and I setup aliases in ts.config.json
{
"compilerOptions": {
"paths": {
"#pkg/*": ["./packages/*"],
},
}
}
in my ts files I can shorten my import paths
// example.ts
import {someThing} from '#pkg/mypackage'
it works fine with tsc and vscode can recognize the alias path correctly.
but when I run npm t whitch runs jest it fails
Cannot find module '#pkg/mypackage' from 'example.ts'
jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
transform: {
"^.+\\.tsx?$": "ts-jest",
},
};
I added this to my package.json file
"jest": {
"moduleNameMapper": {
"#pkg/(.*)": "<rootDir>/packages/$1"
}
}
I managed to use pathsToModuleNameMapper, but I had this issue
https://github.com/kulshekhar/ts-jest/issues/2709
I had the same problem, but managed to get it working by using a couple of plugins. I also have some extra matchers at the end for some additional test types.
My Jest-base.config.js has the tsconfig-paths-jest plugin installed and running. This plugin solved my tsconfig path issues.
I use a common base file for common configuration between unit tests and end to end tests, both of which I run via Jest currently.
jest-base.config.ts
const tsconfig = require('./tsconfig.json');
const moduleNameMapper = require('tsconfig-paths-jest')(tsconfig);
module.exports = {
moduleNameMapper,
preset: 'ts-jest',
testEnvironment: 'node',
rootDir: './',
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/**/*.ts',
'!<rootDir>/**/*.interface.ts',
'!<rootDir>/**/*.mock.ts',
'!<rootDir>/**/*.module.ts',
'!<rootDir>/**/__mock__/*',
'!<rootDir>/src/main.ts'
],
coverageProvider: 'v8',
coverageReporters: [
'clover',
'json',
'lcov',
'text',
'text-summary'
],
resetModules: true,
setupFiles: [
'dotenv/config'
],
// Add the community jest-extended matchers
setupFilesAfterEnv: [
'jest-extended'
],
verbose: false
};
My jest.config.js (for unit tests) will extend my jest-base.config.js to add unit test specific code such as coverage requirements, where to store output for coverage etc.
jest.config.js
const JestBaseConfiguration = require('./jest-base.config');
module.exports = Object.assign(JestBaseConfiguration, {
moduleFileExtensions: ['js', 'json', 'ts'],
testRegex: '.e2e-spec.ts$',
transform: {
'^.+\\.(t|j)s$': 'ts-jest'
},
...

Can I load a file from a bundled javascript application?

I have a node application that when built is bundled all into one file. I want to split out of this bundle the app configuration parameters (it's just a simple object).
The ./build directory becomes populated with only three files: index.js, config.js and a map file.
When I cd into the directory and launch the app with node index.js, I get the following error:
TypeError: Cannot read property 'logPath' of undefined
at Module.<anonymous> (/home/*/repo/build/index.js:1:2367)
at t (/home/*/repo/build/index.js:1:172)
at /home/*/repo/build/index.js:1:964
at Object.<anonymous> (/home/*/repo/build/index.js:1:973)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:191:16)
at bootstrap_node.js:612:3
The top of the built config.js file looks like
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = {
logPath: 'logs',
....
};
The webpack configuration I am using looks as such
const path = require('path');
const nodeExternals = require('webpack-node-externals');
function excludeConfig(context, request, callback) {
/config/.test(request)
? callback(null, 'require("./config.js")', + request)
: callback();
}
module.exports = {
entry: {
index: path.resolve(__dirname, './src/server/index.js')
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: ['node_modules']
}, {
test: /\.js$/,
loader: 'source-map-loader',
enforce: 'pre'
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
modules: [path.resolve(__dirname, 'node_modules')]
},
devtool: 'source-map',
output: {
filename: 'index.js',
path: path.join(__dirname, '/build')
},
mode: 'production',
target: 'node',
externals: [
nodeExternals(),
excludeConfig
],
};
My config file is being built by gulp with the following strategy
const ts = require('gulp-typescript');
const tsProject = ts.createProject('tsconfig.json');
gulp.task('add-config', function () {
return gulp
.src('src/*config.ts')
.pipe(tsProject())
.pipe(gulp.dest('build'));
});
The tsconfig.json file looks as follows:
{
"compilerOptions": {
"outDir": "./build",
"allowJs": true,
"checkJs": false,
"module": "commonjs",
"target": "es5",
"moduleResolution": "node",
"lib": ["es2015", "dom"]
}
}
My hunch is that after the build the configuration file is not providing what the bundle is expecting.
The bundle does contain the following line:
function (e, r) { e.exports = require("./config.js") }
Any ideas on how I can make the bundle load the config.js file?
It seems that after much static analysis, and lack of a comprehensive understanding of the different ways to import modules, for some reason the bundle defines my config file to be a harmony import and attempts to retrieve a default from the file (which in my case I hadn't any).
There are two fixes available (both worked fine):
change the import style away from using defaults by using brackets around the import (I don't know why I prefer this)
import { config } from './../../config';
change the export style and adopt the default route
export default <Config>{
logPath: 'logs',
...
};
To be honest, I don't know why this worked before when this project had still not started the migration to TypeScript. I know that everything babel related was removed before I started so I can't get my head around it.
Webpack Internals
The module loader looks like this:
(function(module, exports) {
module.exports = require("./config.js");
})
It is then passed on to another module and loaded as follows:
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./../../config */ "./../config");
/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_config__WEBPACK_IMPORTED_MODULE_1__);
const PATH = _config__WEBPACK_IMPORTED_MODULE_1___default.a.logPath.replace(/\/*$/, '');
Notice how _config__WEBPACK_IMPORTED_MODULE_1__ already has the contents of my configuration object. However, webpack sends it into a function n which encapsulates the import into a getter function on member a. This encapsulation resolves to using default if the module was marked as an esModule
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};

Categories